Guides

Deploying

Production deployment on Hetzner with Docker Compose.

Server Setup (Hetzner)

  1. Provision a server (recommended: CX32 or larger — 4 vCPU, 8 GB RAM)
  2. Install Docker + Docker Compose
  3. Clone the repository
  4. Configure environment

Production Environment

Create a .env.production with production values:

# Required
GITHUB_TOKEN=ghp_...                    # Private package access
HOST_IP=your-server-ip

# LLM — at least one required for AI features
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...

# Security — generate unique keys
ENCRYPTION_KEY=$(php -r "echo bin2hex(random_bytes(32));")
BLIND_INDEX_KEY=$(php -r "echo bin2hex(random_bytes(32));")

# Tenant
TENANT_SLUG=your-tenant

# App URLs
APP_URL=https://api.yourdomain.com
FRONTEND_URL=https://yourdomain.com

Warning

Never reuse the default encryption keys (dev_encryption_key_32chars_change_me) in production. Generate unique keys for each deployment.

Start Production Stack

docker compose -f docker-compose.yml --env-file .env.production up -d --build

# Initialize
docker exec -it ubios_api php artisan key:generate --force
docker exec -it ubios_api php artisan migrate
docker exec -it ubios_api php artisan db:seed --class=UbiosDatabaseSeeder

SSL / Reverse Proxy

Use Nginx or Caddy as a reverse proxy in front of the Nuxt container:

server {
    listen 443 ssl;
    server_name yourdomain.com;

    ssl_certificate /etc/ssl/yourdomain.crt;
    ssl_certificate_key /etc/ssl/yourdomain.key;

    location / {
        proxy_pass http://127.0.0.1:6080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Backups

PostgreSQL data lives in a Docker volume. Back up regularly:

# Create backup
docker exec ubios_postgres pg_dump -U postgres ubios > backup_$(date +%Y%m%d).sql

# Restore
cat backup_20260410.sql | docker exec -i ubios_postgres psql -U postgres ubios

Monitoring

  • docker ps — check all containers are running
  • docker compose logs -f — live log tailing
  • Agno health: curl http://localhost:8001/health
  • PostgreSQL health: docker inspect ubios_postgres --format='{{.State.Health.Status}}'

Scaling Notes

Each customer gets their own Docker Compose stack. For multiple customers:

  • Use separate Hetzner servers (simplest, most isolated)
  • Or use Docker Compose profiles / separate compose files on a single high-spec server