banatie-service/docs/deployment.md

5.5 KiB

Banatie Production Deployment Guide

Last Updated: December 23, 2025

This guide covers deploying Banatie to a VPS with Docker. For local development, see environment.md.

Overview

Banatie is deployed as an isolated ecosystem with:

  • Landing (Next.js 15.5.9) → banatie.app
  • API (Express.js) → api.banatie.app
  • PostgreSQL (15-alpine) → Database
  • MinIO (SNMD mode) → Object storage

Prerequisites

  • VPS with Docker and Docker Compose
  • Caddy reverse proxy (or similar) with SSL
  • DNS records configured
  • GEMINI_API_KEY from Google AI Studio

Quick Start

# 1. Create directory structure
sudo mkdir -p /opt/banatie/{data,logs,scripts}
sudo mkdir -p /opt/banatie/data/{postgres,minio,waitlist-logs,api-results,api-uploads}
sudo mkdir -p /opt/banatie/data/minio/{drive1,drive2,drive3,drive4}
sudo chown -R $USER:$USER /opt/banatie

# 2. Clone repository
git clone <repo> ~/workspace/projects/banatie-service

# 3. Copy production configs
cp ~/workspace/projects/banatie-service/infrastructure/docker-compose.production.yml /opt/banatie/docker-compose.yml
cp ~/workspace/projects/banatie-service/infrastructure/.env.example /opt/banatie/.env
cp ~/workspace/projects/banatie-service/infrastructure/secrets.env.example /opt/banatie/secrets.env
cp ~/workspace/projects/banatie-service/infrastructure/init-db.sql /opt/banatie/scripts/

# 4. Configure environment
nano /opt/banatie/.env          # Edit public variables
nano /opt/banatie/secrets.env   # Generate and add secrets
chmod 600 /opt/banatie/secrets.env

# 5. Build and start
cd /opt/banatie
docker compose --env-file .env --env-file secrets.env build
docker compose --env-file .env --env-file secrets.env up -d

# 6. Initialize database schema
cd ~/workspace/projects/banatie-service
pnpm install
pnpm --filter @banatie/database db:push

# 7. Create master API key
curl -X POST https://api.banatie.app/api/bootstrap/initial-key
# Or use UI: https://banatie.app/admin/master/

Deploy Scripts

Located in scripts/ directory:

# Deploy landing page
./scripts/deploy-landing.sh           # Normal deploy
./scripts/deploy-landing.sh --no-cache # Fresh build (when deps change)

# Deploy API
./scripts/deploy-api.sh               # Normal deploy
./scripts/deploy-api.sh --no-cache    # Fresh build

Configuration Files

Environment Variables (.env)

NODE_ENV=production
PORT=3000
POSTGRES_DB=banatie_db
POSTGRES_USER=banatie_user
DATABASE_URL=postgresql://banatie_user:<password>@banatie-postgres:5432/banatie_db
MINIO_ENDPOINT=banatie-minio:9000
MINIO_PUBLIC_URL=https://cdn.banatie.app
API_PUBLIC_URL=https://api.banatie.app
NEXT_PUBLIC_API_URL=https://api.banatie.app
CORS_ORIGIN=https://banatie.app,https://api.banatie.app

Secrets (secrets.env)

POSTGRES_PASSWORD=<generated>
MINIO_ROOT_USER=banatie_admin
MINIO_ROOT_PASSWORD=<generated>
MINIO_ACCESS_KEY=banatie_service
MINIO_SECRET_KEY=<generated>
GEMINI_API_KEY=<your-key>
JWT_SECRET=<generated>
SESSION_SECRET=<generated>

Generate secrets with:

openssl rand -base64 32 | tr -d '\n\r '

DNS Configuration

Type Name Value
A @ VPS_IP
CNAME www banatie.app
CNAME api banatie.app
CNAME storage banatie.app
CNAME cdn banatie.app

Caddy Configuration

Add to your Caddyfile:

www.banatie.app {
    redir https://banatie.app{uri} permanent
}

banatie.app {
    reverse_proxy banatie-landing:3000
}

api.banatie.app {
    reverse_proxy banatie-api:3000
}

storage.banatie.app {
    reverse_proxy banatie-minio:9001
}

cdn.banatie.app {
    reverse_proxy banatie-minio:9000
    header Access-Control-Allow-Origin "*"
}

Troubleshooting

Permission Denied on Volumes

Docker User Namespace Remapping offsets UIDs by 165536:

# Fix permissions for Next.js (uid 1001 → 166537)
sudo chown -R 166537:166537 /opt/banatie/data/waitlist-logs

# Fix permissions for API (uid 1001 → 166537)
sudo chown -R 166537:166537 /opt/banatie/data/api-results
sudo chown -R 166537:166537 /opt/banatie/data/api-uploads

Environment Variables Not Applied

Use docker compose up -d instead of docker restart:

docker compose --env-file .env --env-file secrets.env up -d banatie-api

NEXT_PUBLIC_* Variables

Must be set at both build time AND runtime. Ensure NEXT_PUBLIC_API_URL is in .env before building.

The Dockerfiles use simplified single-stage install to avoid symlink issues:

COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
COPY apps/landing ./apps/landing
COPY packages/database ./packages/database
RUN pnpm install --frozen-lockfile
RUN pnpm --filter @banatie/landing build

Database Connection Refused

URL-encode special characters in DATABASE_URL:

  • =%3D
  • @%40
  • #%23

Known Issues

Healthcheck Showing "Unhealthy"

Alpine images don't have curl by default. The healthcheck uses wget but may still show unhealthy in some cases. Services work correctly despite this status.

Next.js Cache Permission Warning

EACCES: permission denied, mkdir '/app/apps/landing/.next/cache'

This is non-critical - images still work, just not cached. To fix:

sudo chown -R 166537:166537 /opt/banatie/data/landing-cache
# And add volume mount for .next/cache

Full VPS Documentation

For complete VPS setup and infrastructure details, see:

  • VPS Repository: VPS/docs/banatie-deployment.md
  • Deployment Manual: VPS/manuals/banatie-service-deployment.md