What Happens When You Type a URL and Press Enter
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →Automate tests, linting, builds and deploys on every push, for free on public repos. A practical GitHub Actions guide with copy-paste workflows, caching, secrets and scheduled jobs.
Continuous integration sounds like something for big teams with DevOps engineers. It isn't. CI simply means "every time I push code, a machine checks it for me." For a solo developer, that's a safety net catching the mistakes you make at 11 p.m. And with GitHub Actions, it's built into the platform where your code already lives.
.github/workflows/ that defines automation.on): what starts it, such as a push, a pull request, a schedule or a manual click.GitHub Actions is free for public repositories on standard runners, and private repositories get a monthly allowance of free minutes. Check GitHub's current billing docs for exact numbers.
Create .github/workflows/ci.yml:
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm run build
Push it, open the Actions tab in your repository, and watch it run. Every pull request now shows a green check or a red cross.
A few notes:
npm ci installs exactly what's in your lockfile. It's faster and more predictable than npm install in CI.cache: npm caches downloaded packages between runs, which speeds things up considerably.@v4 get new major versions over time. Dependabot can keep them updated for you.CI is most useful when it's enforced. In your repository settings, add a branch protection rule (or ruleset) for main:
Now broken code physically can't land on main without someone noticing.
Integration tests often need a database. Actions can run one as a service container:
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 5s
--health-retries 5
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run migrate
- run: npm test
The health check makes the job wait until Postgres is actually ready.
Never put API keys in workflow files. Add them under Settings → Secrets and variables → Actions, then reference them:
- run: npm run deploy
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
GitHub masks secret values in logs. Two more safety notes:
permissions:
contents: read
More on handling secrets: environment variables done right.
Many hosts (Vercel, Netlify, Cloudflare) deploy automatically from Git, so you may not need a deploy step at all. Let CI test and the host deploy.
When you do deploy from Actions, gate it on tests passing and on the right branch:
jobs:
test:
# ... as above
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./scripts/deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
needs: test means deploy only runs if tests pass.
Actions can run on a schedule, which is handy for side projects:
on:
schedule:
- cron: '0 3 * * *' # every day at 03:00 UTC
workflow_dispatch: # plus a manual "Run" button
Use cases:
pg_dump, uploaded to storage.Schedules run in UTC and may start a few minutes late under load. Don't rely on them for precise timing. (Why UTC matters.) Note that scheduled workflows in inactive public repositories can be disabled automatically after a long period without activity.
cache option in setup actions).concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
run: scripts. Pass it through environment variables instead.It's free for public repositories on standard GitHub-hosted runners, and private repositories include a monthly quota of free minutes. Check GitHub's billing documentation for current limits.
CI (continuous integration) automatically tests and builds every change. CD (continuous delivery or deployment) automatically releases changes that pass, either to staging or to production.
Yes. Use on: schedule with a cron expression. Schedules run in UTC and may be delayed slightly during busy periods.
DNS lookups, TCP and TLS handshakes, HTTP requests, servers, and the browser's rendering pipeline. The whole journey of a web page, explained so it finally clicks.
Read →There are dozens of HTTP status codes, but about fifteen cover nearly everything. What each one means, when to return it, and the mix-ups (401 vs 403, 301 vs 302) that confuse everyone.
Read →A, AAAA, CNAME, MX, TXT and NS records in plain English, why DNS changes seem slow, and the troubleshooting commands that tell you what is really going on.
Read →