This week the blog moved from a local Docker Compose setup to a production VPS.
The goal was simple: keep the services reproducible, keep the data intact, and make the deployment repeatable instead of treating the server as a one-off machine.
What moved
The application runs as a set of Compose services: the frontend, authentication service, blog API, mail service, logging service, Postgres, MinIO, and Caddy.
Postgres and MinIO data were transferred to the VPS rather than starting with empty volumes. That preserved the existing users, posts, media, and service data. The Postgres password was rotated during the migration, and the production environment file is kept on the server instead of in the repository.
Caddy handles HTTPS for blog.bjornmagne.com. The public site redirects to the language-specific home page, and the service health checks keep the deployment easy to inspect.
Deployment
The first production run used locally built images to get the site online safely. The repository now also has a registry-based Compose overlay and a GitLab deploy job. Main-branch builds are tagged with the commit SHA, and the deploy job waits for the public HTTPS health check before it considers the release successful.
That gives us a clear rollback path: select the previous image tag, bring the stack back up, and verify the same health endpoint.
A small auth lesson
The data migration exposed one detail that is easy to miss. Auth signing keys are encrypted at rest with a deployment secret. The database and the encrypted keys can move together, but the key-encryption secret must move with them too.
The initial VPS environment used a different secret. Password verification still worked, but auth could not decrypt the signing key to issue an access token. Retiring that unusable migrated key and generating a new VPS-local signing key fixed the problem. Existing sessions were invalidated, which is the right tradeoff after a signing-key change.
What comes next
The remaining deployment work is connecting the GitLab registry and protected CI variables, then running the first owner-approved deploy and rollback exercise. The site is live now, but the deployment process is only complete when a new release and a rollback have both been tested.