Files
pantry/docs/DEPLOYMENT.md
Claw 812c0ace74 docs: Add comprehensive technical documentation
Complete documentation suite:
- DATABASE.md: Full schema, RLS policies, functions, queries
- API.md: Supabase client API, Edge functions, realtime
- DEVELOPMENT.md: Setup, workflow, conventions, testing
- DEPLOYMENT.md: Docker Compose, Coolify, monitoring, backups

Ready for development to begin.
2026-02-08 18:56:46 +00:00

643 lines
12 KiB
Markdown

# Pantry - Deployment Guide
**Version:** 1.0
**Last Updated:** 2026-02-08
---
## 🎯 Deployment Options
### 1. Docker Compose (Recommended)
- Single-server deployment
- All services in one stack
- Easy to self-host
### 2. Coolify
- UI-based deployment
- Automatic SSL via Traefik
- One-click updates
### 3. Manual (Advanced)
- Custom infrastructure
- Kubernetes, etc.
---
## 🐳 Docker Compose Deployment
### Prerequisites
- Docker 24+ & Docker Compose
- Domain name (for SSL)
- Minimum 2GB RAM
- 10GB disk space
### Quick Deploy
```bash
# Clone repository
git clone https://gitea.jeanlucmakiola.de/pantry-app/pantry.git
cd pantry
# Copy production config
cp .env.example .env.production
nano .env.production # Edit variables
# Start services
docker-compose -f docker/docker-compose.prod.yml up -d
# Check status
docker-compose ps
# View logs
docker-compose logs -f
```
### Production Configuration
**docker/docker-compose.prod.yml:**
```yaml
version: '3.8'
services:
# PostgreSQL (Supabase)
postgres:
image: supabase/postgres:15
restart: unless-stopped
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: pantry
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- pantry-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
# Supabase Auth (GoTrue)
auth:
image: supabase/gotrue:v2.151.0
restart: unless-stopped
environment:
GOTRUE_DB_DRIVER: postgres
GOTRUE_DB_DATABASE_URL: postgres://postgres:${DB_PASSWORD}@postgres:5432/pantry
GOTRUE_SITE_URL: ${PUBLIC_APP_URL}
GOTRUE_URI_ALLOW_LIST: ${PUBLIC_APP_URL}
GOTRUE_JWT_SECRET: ${JWT_SECRET}
GOTRUE_JWT_EXP: 3600
# Email/Password
GOTRUE_EXTERNAL_EMAIL_ENABLED: true
# OIDC (optional)
GOTRUE_EXTERNAL_GOOGLE_ENABLED: ${AUTH_GOOGLE_ENABLED:-false}
GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID: ${AUTH_GOOGLE_CLIENT_ID}
GOTRUE_EXTERNAL_GOOGLE_SECRET: ${AUTH_GOOGLE_SECRET}
depends_on:
postgres:
condition: service_healthy
networks:
- pantry-network
# Supabase Realtime
realtime:
image: supabase/realtime:v2.30.23
restart: unless-stopped
environment:
DB_HOST: postgres
DB_NAME: pantry
DB_USER: postgres
DB_PASSWORD: ${DB_PASSWORD}
DB_PORT: 5432
SECRET_KEY_BASE: ${REALTIME_SECRET}
REPLICATION_MODE: RLS
depends_on:
postgres:
condition: service_healthy
networks:
- pantry-network
# Supabase Storage (optional - for product images)
storage:
image: supabase/storage-api:v1.0.6
restart: unless-stopped
environment:
POSTGREST_URL: http://rest:3000
PGRST_JWT_SECRET: ${JWT_SECRET}
DATABASE_URL: postgres://postgres:${DB_PASSWORD}@postgres:5432/pantry
STORAGE_BACKEND: file
FILE_STORAGE_BACKEND_PATH: /var/lib/storage
volumes:
- storage_data:/var/lib/storage
depends_on:
postgres:
condition: service_healthy
networks:
- pantry-network
# PostgREST (Supabase API)
rest:
image: postgrest/postgrest:v12.0.2
restart: unless-stopped
environment:
PGRST_DB_URI: postgres://postgres:${DB_PASSWORD}@postgres:5432/pantry
PGRST_DB_SCHEMAS: public,storage
PGRST_DB_ANON_ROLE: anon
PGRST_JWT_SECRET: ${JWT_SECRET}
PGRST_DB_USE_LEGACY_GUCS: false
depends_on:
postgres:
condition: service_healthy
networks:
- pantry-network
# Pantry App (Nuxt)
app:
build:
context: ../app
dockerfile: Dockerfile
restart: unless-stopped
environment:
SUPABASE_URL: http://rest:3000
SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
PUBLIC_APP_URL: ${PUBLIC_APP_URL}
ports:
- "3000:3000"
depends_on:
- rest
- auth
- realtime
networks:
- pantry-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
# Reverse Proxy (Caddy with auto-SSL)
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
networks:
- pantry-network
volumes:
postgres_data:
storage_data:
caddy_data:
caddy_config:
networks:
pantry-network:
driver: bridge
```
### Environment Variables
**.env.production:**
```bash
# Database
DB_PASSWORD=<generate-strong-password>
# JWT (generate: openssl rand -hex 32)
JWT_SECRET=<generate-strong-jwt-secret>
# Realtime
REALTIME_SECRET=<generate-strong-secret>
# Supabase Keys (generate via supabase CLI or manually)
SUPABASE_ANON_KEY=<your-anon-key>
SUPABASE_SERVICE_KEY=<your-service-key>
# App
PUBLIC_APP_URL=https://pantry.yourdomain.com
# OIDC (optional)
AUTH_GOOGLE_ENABLED=false
AUTH_GOOGLE_CLIENT_ID=
AUTH_GOOGLE_SECRET=
# Open Food Facts
OPENFOODFACTS_API_URL=https://world.openfoodfacts.org
```
### Generate Secrets
```bash
# DB Password
openssl rand -hex 32
# JWT Secret (needs to be the same for all Supabase services)
openssl rand -hex 32
# Supabase Keys (use supabase CLI)
supabase init
supabase start
# Copy anon key and service key from output
```
### Caddy Configuration
**docker/Caddyfile:**
```
{
email your-email@example.com
}
pantry.yourdomain.com {
reverse_proxy app:3000
# WebSocket support (for Supabase Realtime)
@websockets {
header Connection *Upgrade*
header Upgrade websocket
}
reverse_proxy @websockets realtime:4000
}
```
### Database Migrations
```bash
# Apply migrations on first deploy
docker-compose -f docker/docker-compose.prod.yml exec postgres \
psql -U postgres -d pantry -f /migrations/001_schema.sql
# Or use Supabase CLI
supabase db push --db-url postgres://postgres:${DB_PASSWORD}@localhost:5432/pantry
```
### SSL Certificates
**Caddy (automatic):**
- Caddy automatically requests Let's Encrypt certificates
- Renews certificates automatically
**Manual (Certbot):**
```bash
# Install certbot
sudo apt-get install certbot
# Request certificate
sudo certbot certonly --standalone -d pantry.yourdomain.com
# Add to Nginx/Caddy config
```
---
## ☁️ Coolify Deployment
### Prerequisites
- Coolify instance running
- Domain name
- Git repository access
### Deploy Steps
**1. Add Resource in Coolify:**
- Type: Docker Compose
- Source: Git Repository
- Repository: `https://gitea.jeanlucmakiola.de/pantry-app/pantry.git`
- Branch: `main`
- Compose File: `docker/docker-compose.prod.yml`
**2. Configure Environment:**
- Add environment variables from `.env.production`
- Generate secrets via Coolify UI
**3. Set Domain:**
- Domain: `pantry.yourdomain.com`
- SSL: Enable (Let's Encrypt automatic)
**4. Deploy:**
- Click "Deploy"
- Coolify builds and starts services
- Traefik automatically handles SSL
**5. Verify:**
- Visit `https://pantry.yourdomain.com`
- Check logs in Coolify UI
### Coolify-Specific Config
**docker/docker-compose.coolify.yml:**
```yaml
version: '3.8'
services:
# ... (same as prod, but with Coolify labels)
app:
build:
context: ../app
dockerfile: Dockerfile
restart: unless-stopped
environment:
SUPABASE_URL: http://rest:3000
SUPABASE_ANON_KEY: ${SUPABASE_ANON_KEY}
labels:
- "coolify.managed=true"
- "traefik.enable=true"
- "traefik.http.routers.pantry.rule=Host(`${DOMAIN}`)"
- "traefik.http.services.pantry.loadbalancer.server.port=3000"
networks:
- pantry-network
- coolify # Coolify proxy network
networks:
pantry-network:
driver: bridge
coolify:
external: true
```
---
## 📦 App Dockerfile
**app/Dockerfile:**
```dockerfile
FROM oven/bun:1 AS builder
WORKDIR /app
# Install dependencies
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
# Copy source
COPY . .
# Build app
RUN bun run build
# Production image
FROM oven/bun:1-slim
WORKDIR /app
# Copy built app
COPY --from=builder /app/.output /app/.output
# Expose port
EXPOSE 3000
# Start app
CMD ["bun", "run", ".output/server/index.mjs"]
```
---
## 🔧 Post-Deployment
### 1. Apply Migrations
```bash
# Via Supabase CLI
supabase db push --db-url postgres://postgres:PASSWORD@your-server:5432/pantry
# Or manually via psql
psql -h your-server -U postgres -d pantry -f supabase/migrations/001_schema.sql
```
### 2. Seed Default Data
```bash
# Apply seed migrations
psql -h your-server -U postgres -d pantry -f supabase/migrations/002_seed_units.sql
psql -h your-server -U postgres -d pantry -f supabase/migrations/003_seed_tags.sql
```
### 3. Create First User
**Via Supabase Studio:**
- Access at `http://your-server:54323` (if exposed)
- Go to "Authentication" → "Users"
- Add user manually
**Via API:**
```bash
curl -X POST https://pantry.yourdomain.com/auth/v1/signup \
-H "apikey: YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "admin@example.com", "password": "secure-password"}'
```
---
## 📊 Monitoring
### Health Checks
**App:**
```bash
curl https://pantry.yourdomain.com/health
```
**Supabase:**
```bash
curl http://your-server:54321/health
```
### Logs
**Docker Compose:**
```bash
# All services
docker-compose logs -f
# Specific service
docker-compose logs -f app
docker-compose logs -f postgres
```
**Coolify:**
- View logs in Coolify UI
- Real-time log streaming
### Metrics
**Disk Usage:**
```bash
docker system df
```
**Database Size:**
```sql
SELECT
pg_size_pretty(pg_database_size('pantry')) AS db_size;
```
**Container Stats:**
```bash
docker stats
```
---
## 🔄 Updates
### Pull Latest Changes
```bash
# Stop services
docker-compose down
# Pull latest code
git pull origin main
# Rebuild app
docker-compose build app
# Start services
docker-compose up -d
# Apply new migrations
supabase db push
```
### Coolify Auto-Updates
**Enable in Coolify UI:**
- Resource Settings → Auto Deploy
- Trigger: Git push to `main`
- Coolify rebuilds and redeploys automatically
---
## 🔐 Security Checklist
- [ ] Strong passwords for all services
- [ ] JWT secret rotated and secured
- [ ] SSL certificates valid
- [ ] Firewall rules configured (only 80/443 exposed)
- [ ] Database backups enabled
- [ ] Environment variables not committed to git
- [ ] Supabase service key kept secret (not exposed to frontend)
- [ ] Rate limiting configured (if needed)
- [ ] CORS configured properly
---
## 💾 Backup & Restore
### Backup Database
```bash
# Full backup
docker-compose exec postgres pg_dump -U postgres pantry > backup.sql
# Compressed
docker-compose exec postgres pg_dump -U postgres pantry | gzip > backup.sql.gz
# Scheduled backup (cron)
0 2 * * * docker-compose -f /path/to/docker-compose.prod.yml exec postgres pg_dump -U postgres pantry | gzip > /backups/pantry-$(date +\%Y\%m\%d).sql.gz
```
### Restore Database
```bash
# From backup
docker-compose exec -T postgres psql -U postgres pantry < backup.sql
# From compressed
gunzip -c backup.sql.gz | docker-compose exec -T postgres psql -U postgres pantry
```
### Backup Volumes
```bash
# Backup postgres data volume
docker run --rm \
-v pantry_postgres_data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/postgres-data.tar.gz /data
# Restore
docker run --rm \
-v pantry_postgres_data:/data \
-v $(pwd):/backup \
alpine tar xzf /backup/postgres-data.tar.gz -C /
```
---
## 🚨 Troubleshooting
### App won't start
**Check logs:**
```bash
docker-compose logs app
```
**Common issues:**
- Environment variables missing
- Can't connect to Supabase
- Port 3000 already in use
### Database connection failed
**Check PostgreSQL:**
```bash
docker-compose ps postgres
docker-compose logs postgres
```
**Test connection:**
```bash
docker-compose exec postgres psql -U postgres -d pantry -c "SELECT 1;"
```
### SSL not working
**Caddy:**
```bash
# Check Caddy logs
docker-compose logs caddy
# Verify DNS points to server
dig pantry.yourdomain.com
```
**Coolify:**
- Check Traefik logs in Coolify
- Verify domain configuration
### Out of disk space
```bash
# Clean Docker
docker system prune -a
# Clean old images
docker image prune -a
# Clean volumes (careful!)
docker volume prune
```
---
## 📚 Resources
- [Docker Compose Docs](https://docs.docker.com/compose/)
- [Coolify Docs](https://coolify.io/docs)
- [Supabase Self-Hosting](https://supabase.com/docs/guides/self-hosting)
- [Caddy Docs](https://caddyserver.com/docs/)
---
**All documentation complete!** Ready to start development.