Module 1 · Foundations that hold everything up

Lesson 05 — Linux and the terminal

Processes, permissions, logs, environment variables, SSH and scripting to operate your backend.

Published
In this lesson
  1. Exercise 1 — Anatomy of your processes
  2. Exercise 2 — Ports and load
  3. Exercise 3 — The log pipeline
  4. Exercise 4 — Real permissions
  5. Exercise 5 — The well-made.env
  6. Exercise 6 — Local SSH (lab)
  7. Exercise 7 — Your first operations script
  8. Submit

Everything can be done on your local machine (your "test server"). Anything touching production is simulated here. Do not look at solutions.md before submitting.

Setup:

bash
cd ~/dev/ticketflow && source .venv/bin/activate

Exercise 1 — Anatomy of your processes

  1. Start Gunicorn: gunicorn config.wsgi --bind 127.0.0.1:8000 --workers 2 in one terminal.
  2. In another: ps -o pid,ppid,rss,cmd -C gunicorn. Identify the parent process (arbiter) and the 2 workers. How do you tell them apart from the command?
  3. kill -TERM <a_worker_pid>: what happens in Gunicorn's terminal? Does the service keep answering curl? (hint: the arbiter replaces it).
  4. kill -9 <a_worker_pid> and repeat: what difference do you observe in the logs?

Exercise 2 — Ports and load

  1. ss -tlnp | grep -E '8000|5432|6379': which processes listen on each port?
  2. Generate load and watch: top (or htop) while running for i in $(seq 200); do curl -s -o /dev/null http://127.0.0.1:8000/api/health/ & done. How much CPU do the workers consume? And the curl processes?
  3. uptime after the test: how do you read the three numbers on YOUR machine (find the core count with nproc)?

Exercise 3 — The log pipeline

  1. Generate a test log with 100 lines (or use Gunicorn's error.log).
  2. Run and explain each link:
bash
cat access.log | awk '{print $9}' | sort | uniq -c | sort -rn

(what is $9 in an Nginx/Gunicorn access log? which question does it answer?)

  1. Write a pipeline showing the top 5 slowest routes (the longest ones). Hint: if your log has the duration, use sort -k and head; if not, add the duration to the log format first.

Exercise 4 — Real permissions

  1. Create a shell-less user: sudo useradd -r -s /usr/sbin/nologin app_test.
  2. Create a file with a simulated secret and give it 644: echo "SECRET=x" > /tmp/secret.env && chmod 644 /tmp/secret.env. With sudo -u app_test cat /tmp/secret.env, can it read the file? Why?
  3. Switch to chmod 640 and set the app user's group: who can read it now? And with 600?
  4. Write the rule you will apply to your real .env (and run it on your project: chmod 600.env).

Exercise 5 — The well-made.env

  1. In your project, create env.example with every name settings.py uses (no real values).
  2. Verify your real .env is ignored by Git: git check-ignore -v.env (if it isn't, fix it NOW and confirm with git status that it doesn't appear).
  3. Search your history for leaked secrets: git log -p | grep -iE "secret|password|key" | head. If something shows up, note it to rotate it (the security lesson systematizes this).

Exercise 6 — Local SSH (lab)

  1. Generate (if you don't have one) your ed25519 key and test it against localhost: ssh-copy-id your_user@localhost and then ssh your_user@localhost "hostname". Did it get in without a password?
  2. Create ~/.ssh/config with a localtest host pointing to localhost and repeat the command with ssh localtest hostname.
  3. Simulated tunnel: ssh -L 5433:localhost:5432 your_user@localhost in one terminal; in another, check the port: ss -tlnp | grep 5433. Who is listening? What would this allow you with a real remote DB?

Exercise 7 — Your first operations script

  1. Write healthcheck.sh (the one from the lesson) and make it executable (chmod +x). Test with Gunicorn down and up.
  2. Add the broker check: if Redis does not answer (redis-cli ping), the script warns but does not fail (exit 0 with a warning); if the API does not answer, it does fail. Justify the difference in severity.
  3. Have it run every minute in the background with watch -n 60./healthcheck.sh for a while. How would you do it "for real" in production? (hint: systemd timer or cron — note it down, we formalize it in Lesson 31).

Submit

Paste the trimmed outputs and your healthcheck.sh in the chat. Lesson 05 closes and we move to Lesson 06 — Professional Git: from the daily commit to the team flow with branches, rebase and conflicts.