- Next.js
- TypeScript
- Tailwind CSS
- Golang
- PostgreSQL
Production deploys run automatically after CI succeeds on main, or manually via the CD workflow (workflow_dispatch).
The VPS runs the app with Docker Compose. One-time server setup:
- Install Docker and Docker Compose.
- Create a deploy directory (this path becomes
VPS_DEPLOY_PATH). - Add a
.envfile in that directory with the variables used bydeploy/compose.yaml:POSTGRES_USER,POSTGRES_PASSWORD,POSTGRES_DBBACKEND_PORT(default8080),FRONTEND_PORT(default3000)- Backend settings such as
DATABASE_URL,RESEND_API_KEY,SENDER_EMAIL, OAuth credentials, etc. (seebackend/.env.example)
- Log in to GHCR on the VPS so it can pull images:
echo <GITHUB_PAT> | docker login ghcr.io -u <GITHUB_USERNAME> --password-stdin
- Add the deploy SSH public key to
~/.ssh/authorized_keysfor the deploy user.
Secrets (Settings → Secrets and variables → Actions):
| Secret | Description |
|---|---|
VPS_SSH_KEY |
Private SSH key used by the CD workflow to connect to the VPS |
VPS_HOST |
VPS hostname or IP address |
VPS_USER |
SSH user on the VPS |
VPS_DEPLOY_PATH |
Absolute path to the deploy directory on the VPS |
Variables:
| Variable | Description |
|---|---|
NEXT_PUBLIC_API_URL |
Public API URL baked into the frontend image at build time |
On each deploy, the CD workflow:
- Builds and pushes backend and frontend images to
ghcr.io/<owner>/<repo>/backendandghcr.io/<owner>/<repo>/frontend, tagged with the commit SHA andlatest. - Copies
deploy/compose.yamlto the VPS as a template. - Renders the template with the new image tags, pulls images while services are still running, stops services, then starts them. Compose runs migrations as a one-shot container before the backend starts.