# Janua Production Deployment > [!IMPORTANT] > MADFAM-ENCLII-FIRST-LEGACY-RAW v1: This document contains legacy raw infrastructure command examples. > Routine production operations must use Enclii web, API, or CLI. Treat raw > `kubectl`, `helm`, SSH, provider CLI/API, `docker exec`, and direct container > access as platform bootstrap or documented break-glass only, and record any > missing Enclii adapter gap. Production infrastructure documentation for Janua on bare metal (Hetzner). --- ## Infrastructure Overview ``` ┌─────────────────────────────────────────────────────────────────┐ │ Internet │ └──────────────────────────────┬──────────────────────────────────┘ │ ┌──────────────────────────────▼──────────────────────────────────┐ │ Cloudflare (DNS + CDN) │ │ • DNS: chin.ns.cloudflare.com, woz.ns.cloudflare.com │ │ • Proxy: SSL termination, DDoS protection, caching │ └──────────────────────────────┬──────────────────────────────────┘ │ ┌──────────────────────────────▼──────────────────────────────────┐ │ Cloudflare Tunnel │ │ • Runs as systemd service on server │ │ • No exposed ports required │ └──────────────────────────────┬──────────────────────────────────┘ │ ┌──────────────────────────────▼──────────────────────────────────┐ │ Hetzner Bare Metal (enclii-core) │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ janua-api │ │janua-dashb. │ │janua-website│ │ │ │ :4100 │ │ :4101 │ │ :4104 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ janua-docs │ │ postgres │ │ redis │ │ │ │ :4103 │ │ :5432 │ │ :6379 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` > **Port Standard**: Janua uses ports 4100-4199 per the [MADFAM Ecosystem Port Allocation](https://github.com/madfam-org/solarpunk-foundry/blob/main/docs/PORT_ALLOCATION.md). --- ## Server Access ### SSH Connection ```bash # Via Cloudflare Zero Trust tunnel (recommended) ssh ssh.madfam.io # User: solarpunk (use sudo for admin commands) # Requires: cloudflared installed + ~/.ssh/config configured # See: solarpunk-foundry/docs/SSH_ACCESS.md for setup ``` **Server Details:** | Property | Value | |----------|-------| | Control Plane | control-plane node (identity: see the private operations record) | | Worker Node | worker node (identity: see the private operations record) | | SSH Host | ssh.madfam.io | | OS | Ubuntu 24.04 LTS | | Provider | Hetzner | | Location | Helsinki, Finland | ### Project Location ```bash cd /opt/solarpunk/janua ``` --- ## Domain Routing ### Cloudflare Tunnel Configuration **Bare Metal Deployment** (this guide): Routes traffic directly to Docker containers on localhost. | Domain | Target | Service | |--------|--------|---------| | janua.dev | localhost:4104 | Website | | www.janua.dev | localhost:4104 | Website | | api.janua.dev | localhost:4100 | FastAPI Backend | | app.janua.dev | localhost:4101 | Dashboard | | docs.janua.dev | localhost:4103 | Documentation | > **Note for K8s Deployments**: When running on Kubernetes, Cloudflare tunnel should route to K8s Service ports (typically port 80), NOT container ports. For K8s routing configuration, see [janua CLAUDE.md](../../CLAUDE.md#deployment) which documents the correct Service:Port mapping. The K8s Services expose port 80 and forward to the container ports internally via `targetPort`. ### DNS Configuration Nameservers delegated to Cloudflare in Porkbun: - `chin.ns.cloudflare.com` - `woz.ns.cloudflare.com` --- ## Services ### Docker Containers ```bash # View running containers docker ps # View logs docker logs janua-api -f docker logs janua-website -f docker logs janua-dashboard -f # Restart a service docker restart janua-api ``` ### Cloudflare Tunnel ```bash # Check tunnel status systemctl status cloudflared # View tunnel logs journalctl -u cloudflared -f # Restart tunnel systemctl restart cloudflared ``` --- ## Deployment ### Update from Git ```bash cd /opt/solarpunk/janua git pull origin main ``` ### Rebuild Containers ```bash # API (FastAPI) docker build -f Dockerfile.api -t janua/api:latest . docker stop janua-api && docker rm janua-api docker run -d --name janua-api --network janua-network -p 4100:8000 \ --env-file .env.production janua/api:latest # Website (Next.js) docker build -f Dockerfile.website -t janua/website:latest . docker stop janua-website && docker rm janua-website docker run -d --name janua-website --network janua-network -p 4104:3000 \ -e NODE_ENV=production \ -e NEXT_PUBLIC_API_URL=https://api.janua.dev \ -e NEXT_PUBLIC_APP_URL=https://app.janua.dev \ janua/website:latest # Dashboard (Next.js) docker build -f Dockerfile.dashboard -t janua/dashboard:latest . docker stop janua-dashboard && docker rm janua-dashboard docker run -d --name janua-dashboard --network janua-network -p 4101:3000 \ -e NODE_ENV=production \ -e NEXT_PUBLIC_API_URL=https://api.janua.dev \ -e INTERNAL_API_URL=http://janua-api:8000 \ janua/dashboard:latest # Docs (Next.js) docker build -f Dockerfile.docs -t janua/docs:latest . docker stop janua-docs && docker rm janua-docs docker run -d --name janua-docs --network janua-network -p 4103:3000 \ -e NODE_ENV=production \ janua/docs:latest ``` ### Using Docker Compose ```bash cd /opt/solarpunk/janua/deployment/production docker-compose -f docker-compose.production.yml up -d ``` --- ## Monitoring ### Health Checks ```bash # API health (port 4100) curl http://localhost:4100/health # Dashboard health (port 4101) curl http://localhost:4101/health # Docs health (port 4103) curl http://localhost:4103/health # Website health (port 4104) curl http://localhost:4104/health # External (through Cloudflare) curl https://api.janua.dev/health curl https://app.janua.dev curl https://docs.janua.dev curl https://janua.dev ``` ### Resource Usage ```bash # Docker stats docker stats # System resources htop df -h free -h ``` --- ## Troubleshooting ### Container won't start ```bash # Check logs docker logs janua-api --tail 100 # Check if port is in use netstat -tlnp | grep 4100 # API netstat -tlnp | grep 4101 # Dashboard netstat -tlnp | grep 4103 # Docs netstat -tlnp | grep 4104 # Website # Check Docker network docker network inspect janua-network ``` ### Tunnel not routing ```bash # Check tunnel status systemctl status cloudflared # Check tunnel config (ensure ports match MADFAM standard) cat /etc/cloudflared/config.yml # Expected ingress configuration: # ingress: # - hostname: api.janua.dev # service: http://localhost:4100 # - hostname: app.janua.dev # service: http://localhost:4101 # - hostname: docs.janua.dev # service: http://localhost:4103 # - hostname: janua.dev # service: http://localhost:4104 # - hostname: www.janua.dev # service: http://localhost:4104 # - service: http_status:404 # Restart tunnel after config changes systemctl restart cloudflared ``` ### DNS not resolving ```bash # Check with external DNS dig @8.8.8.8 janua.dev dig @1.1.1.1 janua.dev # Expected: Cloudflare IPs (104.x.x.x, 172.x.x.x) ``` --- ## Environment Variables Production environment variables are stored in: - `/opt/solarpunk/janua/.env.production` (main config) - Docker container environment (runtime) **Required variables:** ```bash # Database DATABASE_URL=postgresql://... REDIS_URL=redis://... # Security JWT_SECRET_KEY=... SECRET_KEY=... # Email RESEND_API_KEY=... # Internal INTERNAL_API_KEY=... ``` See `.env.production.example` for full list. --- ## Backup ### Database Backup ```bash # Manual backup docker exec janua-postgres pg_dump -U janua janua > backup_$(date +%Y%m%d).sql # Restore cat backup_20241201.sql | docker exec -i janua-postgres psql -U janua janua ``` ### Redis Backup ```bash # Trigger save docker exec janua-redis redis-cli BGSAVE # Copy RDB file docker cp janua-redis:/data/dump.rdb ./redis_backup_$(date +%Y%m%d).rdb ``` --- ## Security Notes - SSH key authentication only (password auth disabled) - Firewall allows only SSH (22), HTTP (80), HTTPS (443) - All traffic routed through Cloudflare tunnel - No direct port exposure to internet - SSL/TLS handled by Cloudflare --- ## Enclii Integration ### Service Discovery Endpoints For Enclii orchestration, Janua exposes the following endpoints: | Endpoint | URL | Purpose | |----------|-----|---------| | API Health | `http://janua-api:8000/health` | Container health check | | API Docs | `http://janua-api:8000/docs` | OpenAPI specification | | Dashboard | `http://janua-dashboard:3001/` | User management UI | | Website | `http://janua-website:3000/` | Public marketing site | ### Internal Network Configuration ```yaml # Enclii service definition services: janua: api: container: janua-api internal_port: 8000 external_port: 4100 health_check: /health network: janua-network dashboard: container: janua-dashboard internal_port: 3001 external_port: 4101 network: janua-network website: container: janua-website internal_port: 3000 external_port: 4104 network: janua-network # Shared infrastructure dependencies: postgres: container: postgres-shared port: 5432 database: janua redis: container: redis-shared port: 6379 db: 0 ``` ### Environment Variables for Enclii ```bash # Required for Janua API in Enclii deployment ENVIRONMENT=production DATABASE_URL=postgresql://janua:${DB_PASSWORD}@postgres-shared:5432/janua REDIS_URL=redis://:${REDIS_PASSWORD}@redis-shared:6379/0 JWT_SECRET_KEY=${JANUA_JWT_SECRET} SECRET_KEY=${JANUA_SECRET_KEY} EMAIL_PROVIDER=resend RESEND_API_KEY=${RESEND_API_KEY} INTERNAL_API_KEY=${INTERNAL_API_KEY} # Required for Dashboard/Website NEXT_PUBLIC_API_URL=https://api.janua.dev NEXT_PUBLIC_APP_URL=https://app.janua.dev INTERNAL_API_URL=http://janua-api:8000 ``` ### Health Check Integration Enclii can monitor Janua services using: ```bash # API health (returns JSON with status, version, environment) curl -f http://janua-api:8000/health # Dashboard/Website health (HTTP 200 = healthy) curl -f http://janua-dashboard:3001/ curl -f http://janua-website:3000/ ``` --- ## Contact For infrastructure issues, contact the DevOps team or check the `#infrastructure` channel.