The problem
What usually goes wrong
Many small teams deploy by hand: someone connects to a server, pulls the latest code, and restarts a process. It works until it doesn't, and then nobody is sure what is running or how to roll back.
Approach
How we approach it
- 1
Containerise the application with a multi-stage Dockerfile so the production image contains only what it needs.
- 2
Run linting, type checks, and tests on every pull request in GitHub Actions.
- 3
Build and tag an immutable image per commit and push it to a private container registry.
- 4
Promote the same image from staging to production, with a manual approval step before production.
- 5
Use health checks so a failed release is detected and the previous image can be restored quickly.
Architecture
Reference architecture
- 01SourceGit repository with protected main branch and required reviews
- 02CIGitHub Actions: lint, type-check, test, build image
- 03RegistryPrivate registry holding versioned, immutable images
- 04StagingAutomatic deploy of every merge for verification
- 05ProductionApproved promotion of the verified image, behind NGINX with TLS