banatie-service/docs/deployment-plan.md

5.2 KiB

Banatie Production Deployment Plan

Architecture

Isolated Banatie Stack (5 containers):

banatie-landing   - Landing Page (node:20-alpine)
banatie-api       - API Service (node:20-alpine)
banatie-postgres  - PostgreSQL 15 (isolated from VPS PostgreSQL)
banatie-minio     - MinIO storage (SNMD mode)
banatie-minio-init - Initialization (one-shot)

Domains:

banatie.app         → banatie-landing:3000
api.banatie.app     → banatie-api:3000
storage.banatie.app → banatie-minio:9001 (MinIO Console)

File Structure:

~/workspace/projects/banatie-service/   # Git repository (code + config)
├── apps/
│   ├── api-service/Dockerfile
│   └── landing/Dockerfile
├── packages/database/
└── prod-env/
    ├── docker-compose.yml              # Production compose (run from here!)
    ├── .env                            # Config without secrets (in git)
    └── secrets.env                     # Secrets (NOT in git)

/opt/services/data/banatie/             # Runtime data only
├── postgres/                           # PostgreSQL data
├── storage/drive{1-4}/                 # MinIO SNMD
├── logs/                               # API logs
├── results/                            # Generated images
└── uploads/                            # Uploaded files

Docker Images

Container Image Internal Port
banatie-landing Built from apps/landing/Dockerfile 3000
banatie-api Built from apps/api-service/Dockerfile 3000
banatie-postgres postgres:15-alpine 5432
banatie-minio quay.io/minio/minio:latest 9000 (API), 9001 (Console)

Deployment Steps

Step 1: Prepare VPS Directories

# Create runtime data directory
sudo mkdir -p /opt/services/data/banatie/{postgres,logs,results,uploads}
sudo mkdir -p /opt/services/data/banatie/storage/drive{1,2,3,4}
sudo chown -R $USER:$USER /opt/services/data/banatie

Step 2: Configure Repository

cd ~/workspace/projects/banatie-service

# Create secrets.env (not in git)
cp prod-env/secrets.env.example prod-env/secrets.env
nano prod-env/secrets.env
# Fill in: GEMINI_API_KEY, etc.

Step 3: Configure Caddy

Add to /opt/services/configs/caddy/Caddyfile:

banatie.app, www.banatie.app {
    reverse_proxy banatie-landing:3000
    encode gzip
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
        X-Content-Type-Options "nosniff"
        X-Frame-Options "DENY"
    }
}

api.banatie.app {
    reverse_proxy banatie-api:3000
    encode gzip
    header {
        Strict-Transport-Security "max-age=31536000"
        X-Content-Type-Options "nosniff"
    }
}

storage.banatie.app {
    reverse_proxy banatie-minio:9001
    header {
        Strict-Transport-Security "max-age=31536000"
    }
}

Step 4: First Launch

cd ~/workspace/projects/banatie-service/prod-env

# Start stack
docker compose up -d

# Check status
docker compose ps

# View logs
docker compose logs -f

# Restart Caddy to apply config
docker restart caddy

# Create master key
curl -X POST https://api.banatie.app/api/bootstrap/initial-key

Update Process

Update Landing:

cd ~/workspace/projects/banatie-service
git pull origin main
cd prod-env
docker compose up -d --build banatie-landing

Update API:

cd ~/workspace/projects/banatie-service
git pull origin main
cd prod-env
docker compose up -d --build banatie-api

Update Both:

git pull origin main
cd prod-env
docker compose up -d --build banatie-landing banatie-api

Full Restart:

cd ~/workspace/projects/banatie-service/prod-env
docker compose down
docker compose up -d --build

Downtime: ~2-3 minutes for image build


DNS Records (add in GoDaddy)

Record Type Value
banatie.app A 62.146.239.118
www CNAME banatie.app
api A 62.146.239.118
storage A 62.146.239.118

Migration from Static Landing

After successful Docker deployment:

  1. Remove /var/www/banatie.app/ (old static files)
  2. Replace banatie.app section in Caddyfile (static → reverse_proxy)
  3. docker restart caddy

Network Architecture

Internal Network (banatie-internal)

  • All containers communicate internally
  • No external port exposure
  • PostgreSQL and MinIO API only accessible internally

External Network (proxy-network)

  • Shared with Caddy reverse proxy
  • Landing, API, and MinIO Console exposed to Caddy
  • SSL termination at Caddy

Security Notes

  1. No exposed ports - All traffic goes through Caddy with SSL
  2. Isolated PostgreSQL - Separate from VPS main database
  3. Secrets management - secrets.env not tracked in git
  4. MinIO SNMD mode - Erasure coding for data protection

Troubleshooting

Check container status:

cd ~/workspace/projects/banatie-service/prod-env
docker compose ps
docker compose logs banatie-api
docker compose logs banatie-landing

Restart specific service:

docker compose restart banatie-api

Full rebuild:

docker compose down
docker compose build --no-cache
docker compose up -d

Check Caddy logs:

docker logs caddy