VPS deployment

The project runs as one repository and Compose stack on a Linux VPS. API, guest portal, admin, docs, and PostgreSQL run as separate containers. The current Camel Creatives VPS already has host Nginx on ports 80/443, so this deployment uses Docker Compose behind Nginx. Coolify is installed there, but its proxy is not the active 80/443 listener. This avoids conflicting with existing services.

DNS records

Create A records to the VPS public IPv4 address:

NameDestination
api-towers.camelcreatives.comVPS public IP
wateja-towers.camelcreatives.comSame VPS IP
towers.camelcreatives.comSame VPS IP
docs-towers.camelcreatives.comSame VPS IP

Only add AAAA records if IPv6 is correctly configured on the VPS, firewall, and Docker path. DNS must resolve directly to the VPS while Let’s Encrypt issues the certificate.

Firewall and routing

Allow inbound:

The VPS needs outbound HTTPS to provider APIs and ACME, plus outbound UDP 3799 to the AP for CoA. Do not expose TCP 5432 (PostgreSQL), TCP 8000 (Go HTTP), or app container ports. The web containers bind to loopback ports 16440–16443; Nginx proxies to them on the same VPS. RADIUS uses the standard UDP ports 1812/1813, currently filtered at the firewall until the AP’s source address is known.

If the AP uses changing egress IPs, a VPN/static egress address is safer than opening RADIUS widely. If the AP is behind CGNAT, the VPS may not be able to send CoA to its private address; use a routable VPN path or accept that server-driven disconnect is unavailable.

Deploy steps

  1. Install a supported Docker Engine and Compose plugin on the VPS.
  2. Clone this repository into /home/camel/projects/cameltowers.
  3. Copy .env.example to .env, set unique production database, RADIUS, UAM, and encryption secrets, and set file permissions to 0600. Never commit .env.
  4. Set admin bootstrap values with TOWERS_ADMIN_EMAIL, TOWERS_ADMIN_PASSWORD, and origins for the four hostnames. Keep ACCESS_CREDENTIAL_KEY stable and backed up. Set PORTAL_HOST_PORT=16441, ADMIN_HOST_PORT=16440, and DOCS_HOST_PORT=16442.
  5. Copy deploy/nginx/bootstrap.conf to /etc/nginx/sites-available/camel-towers, enable the site, and reload Nginx. Request a Let’s Encrypt certificate for all four hostnames with Certbot’s webroot method.
  6. Copy deploy/nginx/camel-towers.conf into the same Nginx site, install deploy/nginx/reload-after-cert-renewal.sh as a Certbot deploy hook, test the config with nginx -t, then reload Nginx.
  7. Start the stack with the VPS overlay:
docker compose -f docker-compose.yml -f docker-compose.vps.yml up -d --build docker compose ps docker compose logs --tail=100 api admin portal docs
  1. Open https://towers.camelcreatives.com/login and sign in with the bootstrap account. TOTP is optional and can be enrolled from Team & security.
  2. Set the Abliner callback URL to https://api-towers.camelcreatives.com/webhooks/abliner.
  3. Verify HTTPS for all four names and API health at https://api-towers.camelcreatives.com/api/v1/health. Keep paid purchases blocked until the actual AP is registered, its source CIDR is configured, and UAM/RADIUS behavior passes on hardware.

Database and backup

Compose persists PostgreSQL in the towers-postgres named volume. The API’s migrate one-shot service applies lexically sorted SQL migrations and records versions in schema_migrations before API startup.

Back up the database volume using a consistent PostgreSQL backup method. Back up .env/secret-manager values separately, especially ACCESS_CREDENTIAL_KEY, RADIUS and UAM secrets, provider webhook keys, and database credentials. Test restoration before relying on backups.

Local review

The local UI containers bind to loopback ports 3000 (guest), 3001 (admin), and 3002 (docs). The local preview docs URL is http://localhost:3002. The Caddy profile is for a fresh VPS without an existing reverse proxy; this server uses host Nginx and the docker-compose.vps.yml overlay.