Skip to main content
Free courses

Free courses, built from a real production setup

Free courses with animated diagrams, built from a real production setup: DevOps from zero, system design step by step, and Claude Code tips.

By Matthews WongReviewed on

Key takeaway

Short lessons, each built around one animated diagram you step through at your own pace. Every lesson quotes the real configuration that runs this website, says what breaks, and ends with an exercise you can run on your own machine. Free, in English and Indonesian.

Free, no sign-up, no certificate. Built from the setup that runs this website.

Free courses

Pick a track

How a lesson works

Step through the diagram yourself

Every lesson is built around one diagram like this, taken from the first DevOps lesson. Nothing moves until you press Play or step forward, and every step is written out underneath.

From git push to a page in the browser

A push runs verify, then build-and-push, which stores an image in the registry, then deploy, which makes the VPS pull and run it; visitors reach the app through Cloudflare and nginx.

Step 1 of 7. One push, two halves. GitHub Actions turns a commit into a Docker image; the VPS runs it, and visitors reach it through Cloudflare and nginx.

Keys: ← → step · Home / End · K play or pause

  1. One push, two halves. GitHub Actions turns a commit into a Docker image; the VPS runs it, and visitors reach it through Cloudflare and nginx.

  2. A push to main is the event that starts the workflow. On a fresh runner, the verify job installs dependencies, typechecks and lints.

    on: push: branches: [main]
  3. build-and-push needs verify, so it runs only when the checks pass. It builds the image and pushes it to GHCR, tagged latest and with the commit SHA.

    needs: verifyPredict the next step Where do the app's secrets enter on the way to production? (Baked into the image at build time · Written to .env on the server)
  4. deploy needs build-and-push. It connects to the VPS over SSH and writes the runtime settings into .env from GitHub secrets; nothing secret is baked into the image.

    chmod 600 .env
  5. The server pulls the new image, and docker compose up -d replaces the running container with one made from it. That swap is the deploy.

    docker compose pull docker compose up -d --remove-orphans
  6. A visitor's request reaches Cloudflare first, then nginx on the VPS, which terminates TLS and forwards it to the container listening only on localhost.

    # Bind to loopback only — nginx terminates TLS and proxies to this port.Predict the next step Next, a hashed JavaScript file is requested. Who answers it? (Cloudflare, from its cache · nginx on the VPS · The Next.js container)
  7. Hashed static files, like JavaScript chunks, come straight from Cloudflare's cache. HTML always goes to the server, by choice: the same URL can also answer in Markdown.

    cache-control: public, max-age=31536000, immutable cf-cache-status: HIT
Open the full lesson