Nye-TeeOff/docs/vps-deploy-teeoff.md

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:

  • A record for the root domain (@ / empty hostname) -> 85.137.228.98
  • CNAME for www -> 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:

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/tcp
  • 443/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:

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.no redirects to HTTPS
  • https://www.teeoff.no redirects to https://teeoff.no
  • https://teeoff.no/api/health returns 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