3.7 KiB
TeeOff VPS deployment
This project can be served on https://teeoff.no with Docker Compose and Caddy.
1. DNS at one.com
Create or update these records:
Arecord for the root domain (@/ empty hostname) ->85.137.228.98CNAMEforwww->teeoff.no
If teeoff.no currently has old A, AAAA, web-forward, or alias records pointing elsewhere, remove or replace them.
If you do not actively use IPv6 on the VPS, remove any stale AAAA record for teeoff.no and www.
one.com documents A/CNAME management here:
- https://help.one.com/hc/en-us/articles/360000799298-How-do-I-create-an-A-record
- https://help.one.com/hc/en-us/articles/360000803517-How-do-I-create-a-CNAME-record
2. Required environment values
On the VPS, create .env in the project root and make sure these values are correct:
PUBLIC_BASE_URL=https://teeoff.no
NEXT_PUBLIC_SITE_URL=https://teeoff.no
DATABASE_URL=postgresql://teeoff_admin:...@db:5432/teeoff
POSTGRES_USER=teeoff_admin
POSTGRES_PASSWORD=...
POSTGRES_DB=teeoff
JWT_SECRET=...
PUBLIC_SESSION_SECRET=...
ACME_EMAIL=you@example.com
If public comments or Google login are used, keep the SMTP and Google OAuth values configured too.
Important Google OAuth update:
- Authorized redirect URI should include
https://teeoff.no/api/public/auth/google/callback
3. Start the production stack
Use the production compose file:
docker compose -f docker-compose.prod.yml up -d --build
This stack exposes only:
80/tcp443/tcp
The app containers stay internal and are reached through Caddy only.
4. Reverse proxy layout
The Caddy config is in deploy/Caddyfile.
Routing is:
https://teeoff.no/api/admin/uploads/images-> Next.js- all other
https://teeoff.no/api/*-> FastAPI - everything else -> Next.js frontend
That exception matters because the image upload endpoint is implemented in Next.js, while the rest of the API lives in FastAPI.
The /api prefix must be preserved when proxying, because both apps define routes with /api/... paths.
5. VPS hardening
Recommended minimum host steps on Ubuntu 24.04:
sudo apt update && sudo apt upgrade -y
sudo apt install -y ufw fail2ban
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
Important Docker note: published Docker ports can bypass ufw if you expose them directly on the host. That is why the production compose file publishes only Caddy's 80 and 443.
Useful official references:
- Ubuntu UFW docs: https://ubuntu.com/server/docs/how-to/security/firewalls/
- Docker firewall warning: https://docs.docker.com/engine/network/packet-filtering-firewalls/
- Docker port publishing warning: https://docs.docker.com/engine/network/port-publishing/
- Caddy automatic HTTPS: https://caddyserver.com/docs/automatic-https
6. SSH hardening
Do this after you have verified SSH key login works:
- create a non-root sudo user if you do not already use one
- disable password authentication in
/etc/ssh/sshd_config - disable root SSH login if you do not need it
- restart SSH
Typical settings:
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
Then:
sudo systemctl restart ssh
7. Verification checklist
After DNS has propagated and the stack is up:
curl -I http://teeoff.no
curl -I https://teeoff.no
curl -I https://www.teeoff.no
curl -I https://teeoff.no/api/health
Expected results:
http://teeoff.noredirects to HTTPShttps://www.teeoff.noredirects tohttps://teeoff.nohttps://teeoff.no/api/healthreturns the API health payload
8. Existing compose file
docker-compose.yml is still useful for the earlier/local setup.
For the VPS cutover, prefer:
docker compose -f docker-compose.prod.yml up -d --build