How to deploy the site to production. This guide assumes your changes are
already merged to main and verified on QA.
For background on environments and secrets, see 08-ENVIRONMENTS.md.
main.If any of these are not true, fix them first. Do not deploy broken code to production.
Open the QA site and check that:
This is a manual check. There is no automated QA gate beyond CI.
main branch (default).gh workflow run "Deploy to Production"
After triggering, the workflow pauses and waits for approval from a required reviewer.
If you are a required reviewer:
production environment and click Approve and deploy.gh run list --workflow="Deploy to Production" --limit 1
Note the run ID, then:
gh run view <run-id>
Approval must be done in the web UI — the CLI does not support approving environment deployments.
Once the workflow completes (typically under 2 minutes):
If something is wrong on production after a deploy:
Find the last known good commit on main:
git log --oneline -10
If the problem was introduced in the latest merge, revert it:
git revert <merge-commit-hash>
git push
After the revert merges, trigger a new production deploy (Step 2 above).
The deploy uses a zero-downtime swap. The previous release is kept on the server as a backup directory. If you have SSH access, you can swap back manually — but prefer the git revert approach when possible.
Before planning a release, check what has been deployed:
# Fetch tags from remote (local clone may not have them)
git fetch --tags
# Show the five most recent release tags
git tag --sort=-v:refname | head -5
# Show all PRs merged since the last minor/major release (e.g. v1.0.0)
git log v1.0.0..HEAD --oneline --merges
# Or use the GitHub CLI to list releases
gh release list --limit 5
The first step — git fetch --tags — is essential. Tags are created by
the deploy workflow on the remote; a local clone will not have them
unless you fetch explicitly.
Every production deploy is automatically tagged and the version is shown in the site footer. No manual tagging is needed for regular deploys.
VERSION file in the project root contains the major.minor
version (e.g. 1.0).v1.0.* tag, increments the patch number, and builds the site with
that version in the footer (e.g. v1.0.4).v1.0.4).The footer shows different version strings per environment:
| Environment | Example | How |
|---|---|---|
| Production | v1.0.4 |
Auto-incremented patch from git tags |
| QA (after PR merge) | v1.0.4 – QA PR212 |
Latest production version + PR number from merge commit |
| QA (after prod deploy) | v1.0.4 |
Exact production version — confirms QA matches prod |
| Local | v1.0 – Lokal 2026-03-02 14:30 |
Base version + build timestamp |
Event-data deploys do not update the version in the footer.
v1.0.1) — auto-incremented on every production deploy.
No manual action needed.v1.1.0) — new features, new pages, new form fields.
Bump when one or more user-visible features have been added since the
last minor release.v2.0.0) — breaking changes, major redesigns.
Bump when the site has been fundamentally restructured or redesigned
in a way that would surprise returning users.A minor bump is appropriate when the set of changes since the last
.0 release feels like a meaningful milestone — for example, a new
page, a new schedule view, or a new form field. There is no fixed
threshold; use judgement. When in doubt, a minor bump is better than
letting patch numbers grow indefinitely.
Before bumping the version, review what has changed and draft release notes. This makes the automatically generated GitHub Release more useful and ensures nothing is forgotten.
.0 releasegit fetch --tags
# Find the base tag for the current version series
git tag --sort=-v:refname | grep 'v1\.0\.' | tail -1 # e.g. v1.0.0
# List merged PRs since that tag
git log v1.0.0..HEAD --oneline --merges
# Or for a more detailed diff
git log v1.0.0..HEAD --oneline
Replace v1.0.0 with the actual base tag of the current version
series — this is the .0 tag, not the latest patch.
Write a short summary of the release. Group changes by category:
Keep it concise — a few bullet points per category. The audience is someone who uses the site, not a developer.
VERSION file — change 1.0 to 1.1 (or 2.0).chore: bump version to 1.1v1.1.* tags exist, tags as v1.1.0, and
automatically creates a GitHub Release with auto-generated notes
listing all PRs since the previous release.After the deploy creates the release, open it on GitHub and replace or supplement the auto-generated notes with your drafted summary from step 2. This is the version that people will actually read.
Patch-only deploys (the normal case) create only a tag, not a Release.
Merge to main
→ CI passes
→ QA auto-deploys (footer: v1.0.3 – QA PR212)
→ You verify on QA
→ You trigger "Deploy to Production"
→ Approver approves
→ Production deploys (footer: v1.0.4)
→ Git tag v1.0.4 created automatically
→ QA auto-redeploys (footer: v1.0.4 — matches prod)
→ You verify on production
For event data submitted via the form, both QA and Production update automatically — no manual step needed.