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.
643 lines
12 KiB
Markdown
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.
|