Initial commit: MOPC platform with Docker deployment setup
Full Next.js 15 platform with tRPC, Prisma, PostgreSQL, NextAuth. Includes production Dockerfile (multi-stage, port 7600), docker-compose with registry-based image pull, Gitea Actions CI workflow, nginx config for portal.monaco-opc.com, deployment scripts, and DEPLOYMENT.md guide. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
651
docs/architecture/infrastructure.md
Normal file
651
docs/architecture/infrastructure.md
Normal file
@@ -0,0 +1,651 @@
|
||||
# MOPC Platform - Infrastructure
|
||||
|
||||
## Overview
|
||||
|
||||
The MOPC platform is self-hosted on a Linux VPS at **monaco-opc.com** with the following architecture:
|
||||
|
||||
- **Nginx** (host-level) - Reverse proxy with SSL termination
|
||||
- **Docker Compose** (MOPC stack) - Next.js + PostgreSQL
|
||||
- **MinIO** (separate stack) - S3-compatible file storage
|
||||
- **Poste.io** (separate stack) - Self-hosted email server
|
||||
|
||||
**Key Configurations:**
|
||||
- Max file size: 500MB (for video uploads)
|
||||
- SSL via Certbot (Let's Encrypt)
|
||||
|
||||
## Architecture Diagram
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ INTERNET │
|
||||
└─────────────────────────────────┬────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ LINUX VPS │
|
||||
├──────────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ NGINX (Host Level) │ │
|
||||
│ │ │ │
|
||||
│ │ - SSL termination via Certbot │ │
|
||||
│ │ - Reverse proxy to Docker services │ │
|
||||
│ │ - Rate limiting │ │
|
||||
│ │ - Security headers │ │
|
||||
│ │ │ │
|
||||
│ │ Ports: 80 (HTTP → HTTPS redirect), 443 (HTTPS) │ │
|
||||
│ └─────────────────────────────────┬──────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────────────────┼───────────────────────┐ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
|
||||
│ │ MOPC Stack │ │ MinIO Stack │ │ Poste.io Stack │ │
|
||||
│ │ (Docker Compose)│ │ (Docker Compose)│ │ (Docker Compose)│ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ ┌────────────┐ │ │ ┌────────────┐ │ │ ┌────────────┐ │ │
|
||||
│ │ │ Next.js │ │ │ │ MinIO │ │ │ │ Poste.io │ │ │
|
||||
│ │ │ :3000 │ │ │ │ :9000 │ │ │ │ :25,587 │ │ │
|
||||
│ │ └────────────┘ │ │ │ :9001 │ │ │ └────────────┘ │ │
|
||||
│ │ │ │ └────────────┘ │ │ │ │
|
||||
│ │ ┌────────────┐ │ │ │ │ │ │
|
||||
│ │ │ PostgreSQL │ │ │ │ │ │ │
|
||||
│ │ │ :5432 │ │ │ │ │ │ │
|
||||
│ │ └────────────┘ │ │ │ │ │ │
|
||||
│ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
|
||||
│ │
|
||||
│ Data Volumes: │
|
||||
│ /data/mopc/postgres /data/minio /data/poste │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Docker Compose Configuration
|
||||
|
||||
### MOPC Stack
|
||||
|
||||
```yaml
|
||||
# docker/docker-compose.yml
|
||||
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile
|
||||
container_name: mopc-app
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- DATABASE_URL=postgresql://mopc:${DB_PASSWORD}@postgres:5432/mopc
|
||||
- NEXTAUTH_URL=${NEXTAUTH_URL}
|
||||
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
|
||||
- MINIO_ENDPOINT=${MINIO_ENDPOINT}
|
||||
- MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}
|
||||
- MINIO_SECRET_KEY=${MINIO_SECRET_KEY}
|
||||
- MINIO_BUCKET=${MINIO_BUCKET}
|
||||
- SMTP_HOST=${SMTP_HOST}
|
||||
- SMTP_PORT=${SMTP_PORT}
|
||||
- SMTP_USER=${SMTP_USER}
|
||||
- SMTP_PASS=${SMTP_PASS}
|
||||
- EMAIL_FROM=${EMAIL_FROM}
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- mopc-network
|
||||
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: mopc-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_USER=mopc
|
||||
- POSTGRES_PASSWORD=${DB_PASSWORD}
|
||||
- POSTGRES_DB=mopc
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U mopc"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
networks:
|
||||
- mopc-network
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: /data/mopc/postgres
|
||||
|
||||
networks:
|
||||
mopc-network:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
### Development Stack
|
||||
|
||||
The development stack includes PostgreSQL, MinIO, and the Next.js app running in Docker containers.
|
||||
|
||||
```yaml
|
||||
# docker/docker-compose.dev.yml
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: mopc-postgres-dev
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
- POSTGRES_USER=mopc
|
||||
- POSTGRES_PASSWORD=devpassword
|
||||
- POSTGRES_DB=mopc
|
||||
volumes:
|
||||
- postgres_dev_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U mopc"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
minio:
|
||||
image: minio/minio
|
||||
container_name: mopc-minio-dev
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
environment:
|
||||
- MINIO_ROOT_USER=minioadmin
|
||||
- MINIO_ROOT_PASSWORD=minioadmin
|
||||
volumes:
|
||||
- minio_dev_data:/data
|
||||
command: server /data --console-address ":9001"
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
interval: 30s
|
||||
timeout: 20s
|
||||
retries: 3
|
||||
|
||||
# MinIO client to create default bucket on startup
|
||||
createbuckets:
|
||||
image: minio/mc
|
||||
depends_on:
|
||||
minio:
|
||||
condition: service_healthy
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
mc alias set myminio http://minio:9000 minioadmin minioadmin;
|
||||
mc mb --ignore-existing myminio/mopc-files;
|
||||
mc anonymous set download myminio/mopc-files;
|
||||
echo 'Bucket created successfully';
|
||||
"
|
||||
|
||||
# Next.js application
|
||||
app:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.dev
|
||||
container_name: mopc-app-dev
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://mopc:devpassword@postgres:5432/mopc
|
||||
- NEXTAUTH_URL=http://localhost:3000
|
||||
- NEXTAUTH_SECRET=dev-secret-key-for-local-development-only
|
||||
- MINIO_ENDPOINT=http://minio:9000
|
||||
- MINIO_ACCESS_KEY=minioadmin
|
||||
- MINIO_SECRET_KEY=minioadmin
|
||||
- MINIO_BUCKET=mopc-files
|
||||
- NODE_ENV=development
|
||||
volumes:
|
||||
- ../src:/app/src
|
||||
- ../public:/app/public
|
||||
- ../prisma:/app/prisma
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
minio:
|
||||
condition: service_healthy
|
||||
|
||||
volumes:
|
||||
postgres_dev_data:
|
||||
minio_dev_data:
|
||||
```
|
||||
|
||||
### Quick Start (Development)
|
||||
|
||||
```bash
|
||||
# 1. Start all services (PostgreSQL, MinIO, Next.js)
|
||||
docker compose -f docker/docker-compose.dev.yml up --build -d
|
||||
|
||||
# 2. Push database schema
|
||||
docker exec mopc-app-dev npx prisma db push
|
||||
|
||||
# 3. Seed test data
|
||||
docker exec mopc-app-dev npx tsx prisma/seed.ts
|
||||
|
||||
# 4. Open http://localhost:3000
|
||||
# Login with: admin@monaco-opc.com (magic link)
|
||||
```
|
||||
|
||||
### Development URLs
|
||||
|
||||
| Service | URL | Credentials |
|
||||
|---------|-----|-------------|
|
||||
| Next.js App | http://localhost:3000 | See seed data |
|
||||
| MinIO Console | http://localhost:9001 | minioadmin / minioadmin |
|
||||
| PostgreSQL | localhost:5432 | mopc / devpassword |
|
||||
|
||||
### Test Accounts (after seeding)
|
||||
|
||||
| Role | Email |
|
||||
|------|-------|
|
||||
| Super Admin | admin@monaco-opc.com |
|
||||
| Jury Member | jury1@example.com |
|
||||
| Jury Member | jury2@example.com |
|
||||
| Jury Member | jury3@example.com |
|
||||
|
||||
## Dockerfile
|
||||
|
||||
```dockerfile
|
||||
# docker/Dockerfile
|
||||
|
||||
FROM node:20-alpine AS base
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM base AS deps
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json package-lock.json* ./
|
||||
RUN npm ci
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
# Generate Prisma client
|
||||
RUN npx prisma generate
|
||||
|
||||
# Build Next.js
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
RUN npm run build
|
||||
|
||||
# Production image
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV production
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
COPY --from=builder /app/public ./public
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
COPY --from=builder /app/.next/static ./.next/static
|
||||
COPY --from=builder /app/prisma ./prisma
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
ENV PORT 3000
|
||||
ENV HOSTNAME "0.0.0.0"
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
```
|
||||
|
||||
## Nginx Configuration
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/sites-available/mopc-platform
|
||||
|
||||
# Rate limiting zone
|
||||
limit_req_zone $binary_remote_addr zone=mopc_limit:10m rate=10r/s;
|
||||
|
||||
# MOPC Platform
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name monaco-opc.com;
|
||||
|
||||
# SSL certificates (managed by Certbot)
|
||||
ssl_certificate /etc/letsencrypt/live/monaco-opc.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/monaco-opc.com/privkey.pem;
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self';" always;
|
||||
|
||||
# File upload size (500MB for videos)
|
||||
client_max_body_size 500M;
|
||||
|
||||
# Rate limiting
|
||||
limit_req zone=mopc_limit burst=20 nodelay;
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/mopc-access.log;
|
||||
error_log /var/log/nginx/mopc-error.log;
|
||||
|
||||
# Next.js application
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
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;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
|
||||
# Timeouts for large file uploads
|
||||
proxy_connect_timeout 300s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
# Static files caching
|
||||
location /_next/static {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_cache_valid 200 365d;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /api/health {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
access_log off;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTP to HTTPS redirect
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name monaco-opc.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
## SSL Setup with Certbot
|
||||
|
||||
```bash
|
||||
# Install Certbot
|
||||
sudo apt update
|
||||
sudo apt install certbot python3-certbot-nginx
|
||||
|
||||
# Obtain certificate
|
||||
sudo certbot --nginx -d monaco-opc.com
|
||||
|
||||
# Auto-renewal is configured automatically
|
||||
# Test renewal
|
||||
sudo certbot renew --dry-run
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Production (.env)
|
||||
|
||||
```env
|
||||
# Application
|
||||
NODE_ENV=production
|
||||
NEXTAUTH_URL=https://monaco-opc.com
|
||||
NEXTAUTH_SECRET=generate-a-secure-random-string-here
|
||||
|
||||
# Database
|
||||
DB_PASSWORD=your-secure-database-password
|
||||
DATABASE_URL=postgresql://mopc:${DB_PASSWORD}@postgres:5432/mopc
|
||||
|
||||
# MinIO (external stack)
|
||||
MINIO_ENDPOINT=http://localhost:9000
|
||||
MINIO_ACCESS_KEY=your-minio-access-key
|
||||
MINIO_SECRET_KEY=your-minio-secret-key
|
||||
MINIO_BUCKET=mopc-files
|
||||
|
||||
# Email (Poste.io)
|
||||
SMTP_HOST=localhost
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=noreply@monaco-opc.com
|
||||
SMTP_PASS=your-smtp-password
|
||||
EMAIL_FROM=MOPC Platform <noreply@monaco-opc.com>
|
||||
```
|
||||
|
||||
### Generate Secrets
|
||||
|
||||
```bash
|
||||
# Generate NEXTAUTH_SECRET
|
||||
openssl rand -base64 32
|
||||
|
||||
# Generate DB_PASSWORD
|
||||
openssl rand -base64 24
|
||||
```
|
||||
|
||||
## Deployment Commands
|
||||
|
||||
### Initial Deployment
|
||||
|
||||
```bash
|
||||
# 1. Clone repository
|
||||
git clone https://github.com/your-org/mopc-platform.git /opt/mopc
|
||||
cd /opt/mopc
|
||||
|
||||
# 2. Create environment file
|
||||
cp .env.example .env
|
||||
nano .env # Edit with production values
|
||||
|
||||
# 3. Create data directories
|
||||
sudo mkdir -p /data/mopc/postgres
|
||||
sudo chown -R 1000:1000 /data/mopc
|
||||
|
||||
# 4. Start the stack
|
||||
cd docker
|
||||
docker compose up -d
|
||||
|
||||
# 5. Run database migrations
|
||||
docker compose exec app npx prisma migrate deploy
|
||||
|
||||
# 6. Seed initial data (optional)
|
||||
docker compose exec app npx prisma db seed
|
||||
|
||||
# 7. Enable Nginx site
|
||||
sudo ln -s /etc/nginx/sites-available/mopc-platform /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
|
||||
# 8. Set up SSL
|
||||
sudo certbot --nginx -d monaco-opc.com
|
||||
```
|
||||
|
||||
### Updates
|
||||
|
||||
```bash
|
||||
cd /opt/mopc
|
||||
|
||||
# 1. Pull latest code
|
||||
git pull origin main
|
||||
|
||||
# 2. Rebuild and restart
|
||||
cd docker
|
||||
docker compose build app
|
||||
docker compose up -d app
|
||||
|
||||
# 3. Run any new migrations
|
||||
docker compose exec app npx prisma migrate deploy
|
||||
```
|
||||
|
||||
### Rollback
|
||||
|
||||
```bash
|
||||
# Revert to previous image
|
||||
docker compose down
|
||||
git checkout HEAD~1
|
||||
docker compose build app
|
||||
docker compose up -d
|
||||
|
||||
# Or restore from specific tag
|
||||
git checkout v1.0.0
|
||||
docker compose build app
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Backup Strategy
|
||||
|
||||
### Database Backups
|
||||
|
||||
```bash
|
||||
# Create backup script
|
||||
cat > /opt/mopc/scripts/backup-db.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
BACKUP_DIR=/data/backups/mopc
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_FILE=$BACKUP_DIR/mopc_$DATE.sql.gz
|
||||
|
||||
mkdir -p $BACKUP_DIR
|
||||
|
||||
docker exec mopc-postgres pg_dump -U mopc mopc | gzip > $BACKUP_FILE
|
||||
|
||||
# Keep last 30 days
|
||||
find $BACKUP_DIR -name "mopc_*.sql.gz" -mtime +30 -delete
|
||||
|
||||
echo "Backup completed: $BACKUP_FILE"
|
||||
EOF
|
||||
|
||||
chmod +x /opt/mopc/scripts/backup-db.sh
|
||||
|
||||
# Add to crontab (daily at 2 AM)
|
||||
echo "0 2 * * * /opt/mopc/scripts/backup-db.sh >> /var/log/mopc-backup.log 2>&1" | sudo tee -a /etc/cron.d/mopc-backup
|
||||
```
|
||||
|
||||
### Restore Database
|
||||
|
||||
```bash
|
||||
# Restore from backup
|
||||
gunzip < /data/backups/mopc/mopc_20260115_020000.sql.gz | docker exec -i mopc-postgres psql -U mopc mopc
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Health Check Endpoint
|
||||
|
||||
```typescript
|
||||
// src/app/api/health/route.ts
|
||||
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Check database connection
|
||||
await prisma.$queryRaw`SELECT 1`
|
||||
|
||||
return Response.json({
|
||||
status: 'healthy',
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
} catch (error) {
|
||||
return Response.json(
|
||||
{
|
||||
status: 'unhealthy',
|
||||
error: 'Database connection failed',
|
||||
},
|
||||
{ status: 503 }
|
||||
)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Log Viewing
|
||||
|
||||
```bash
|
||||
# Application logs
|
||||
docker compose logs -f app
|
||||
|
||||
# Nginx access logs
|
||||
tail -f /var/log/nginx/mopc-access.log
|
||||
|
||||
# Nginx error logs
|
||||
tail -f /var/log/nginx/mopc-error.log
|
||||
|
||||
# PostgreSQL logs
|
||||
docker compose logs -f postgres
|
||||
```
|
||||
|
||||
### Resource Monitoring
|
||||
|
||||
```bash
|
||||
# Docker stats
|
||||
docker stats mopc-app mopc-postgres
|
||||
|
||||
# System resources
|
||||
htop
|
||||
```
|
||||
|
||||
## Security Checklist
|
||||
|
||||
- [ ] SSL certificate active and auto-renewing
|
||||
- [ ] Database password is strong and unique
|
||||
- [ ] NEXTAUTH_SECRET is randomly generated
|
||||
- [ ] MinIO credentials are secure
|
||||
- [ ] SMTP credentials are secure
|
||||
- [ ] Firewall allows only ports 80, 443, 22
|
||||
- [ ] Docker daemon not exposed to network
|
||||
- [ ] Regular backups configured
|
||||
- [ ] Log rotation configured
|
||||
- [ ] Security headers enabled in Nginx
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Application Won't Start
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
docker compose logs app
|
||||
|
||||
# Check if database is ready
|
||||
docker compose exec postgres pg_isready -U mopc
|
||||
|
||||
# Restart stack
|
||||
docker compose restart
|
||||
```
|
||||
|
||||
### Database Connection Issues
|
||||
|
||||
```bash
|
||||
# Test connection from app container
|
||||
docker compose exec app sh
|
||||
nc -zv postgres 5432
|
||||
|
||||
# Check PostgreSQL logs
|
||||
docker compose logs postgres
|
||||
```
|
||||
|
||||
### SSL Certificate Issues
|
||||
|
||||
```bash
|
||||
# Test certificate
|
||||
sudo certbot certificates
|
||||
|
||||
# Force renewal
|
||||
sudo certbot renew --force-renewal
|
||||
|
||||
# Check Nginx configuration
|
||||
sudo nginx -t
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Database Design](./database.md) - Schema and migrations
|
||||
- [API Design](./api.md) - tRPC endpoints
|
||||
Reference in New Issue
Block a user