Skip to main content

Overview

Attendee can be deployed using various methods depending on your infrastructure requirements. This guide covers Docker-based deployments, Kubernetes, and best practices for production.

Docker Deployment

Building the Production Image

The multi-stage Dockerfile optimizes the image for production:
# Build the production image
docker build -t attendee:latest .

Dockerfile Stages

The build process uses three stages:
1

Base Stage

Installs system dependencies, Chrome, PulseAudio, and media processing libraries.Key dependencies:
  • Ubuntu 22.04 (amd64)
  • Google Chrome 134.0.6998.88
  • ChromeDriver 134.0.6998.88
  • PulseAudio, ALSA, FFmpeg
  • OpenCV, GStreamer
2

Dependencies Stage

Installs Python dependencies from requirements.txt.
  • Adds Tini as init system
  • Caches Python packages for faster rebuilds
3

Build Stage

Creates non-root user and sets up the application.
  • Creates app user (UID 1000)
  • Sets proper file permissions
  • Configures Chrome policies symlink
  • Sets entrypoint script

Production Docker Compose

Create a docker-compose.yaml for production:
docker-compose.yaml
version: '3.8'

services:
  attendee-app:
    image: attendee:latest
    restart: unless-stopped
    ports:
      - "8000:8000"
    environment:
      - POSTGRES_HOST=postgres
      - REDIS_URL=redis://redis:6379/0
      - DJANGO_SETTINGS_MODULE=attendee.settings.production
      - SECRET_KEY=${SECRET_KEY}
      - ALLOWED_HOSTS=${ALLOWED_HOSTS}
      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
      - AWS_STORAGE_BUCKET_NAME=${AWS_STORAGE_BUCKET_NAME}
    command: gunicorn attendee.wsgi:application --bind 0.0.0.0:8000 --workers 4
    depends_on:
      - postgres
      - redis
    networks:
      - attendee_network

  attendee-worker:
    image: attendee:latest
    restart: unless-stopped
    environment:
      - POSTGRES_HOST=postgres
      - REDIS_URL=redis://redis:6379/0
      - DJANGO_SETTINGS_MODULE=attendee.settings.production
      - SECRET_KEY=${SECRET_KEY}
      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
      - AWS_STORAGE_BUCKET_NAME=${AWS_STORAGE_BUCKET_NAME}
      - ENABLE_CHROME_SANDBOX=false
    command: celery -A attendee worker -l INFO --concurrency=4
    depends_on:
      - postgres
      - redis
    networks:
      - attendee_network

  attendee-scheduler:
    image: attendee:latest
    restart: unless-stopped
    environment:
      - POSTGRES_HOST=postgres
      - REDIS_URL=redis://redis:6379/0
      - DJANGO_SETTINGS_MODULE=attendee.settings.production
      - SECRET_KEY=${SECRET_KEY}
    command: python manage.py run_scheduler
    entrypoint: []
    depends_on:
      - postgres
      - redis
    networks:
      - attendee_network

  postgres:
    image: postgres:15.3-alpine
    restart: unless-stopped
    environment:
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - PGDATA=/var/lib/postgresql/data/pgdata
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - attendee_network

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    networks:
      - attendee_network

networks:
  attendee_network:
    driver: bridge

volumes:
  postgres_data:
  redis_data:

Starting Production Services

1

Create Environment File

Create a .env file with production variables:
.env
SECRET_KEY=your_production_secret_key
ALLOWED_HOSTS=attendee.yourdomain.com
POSTGRES_DB=attendee_production
POSTGRES_USER=attendee_user
POSTGRES_PASSWORD=secure_password_here
AWS_ACCESS_KEY_ID=your_key
AWS_SECRET_ACCESS_KEY=your_secret
AWS_STORAGE_BUCKET_NAME=attendee-production
Never commit the .env file to version control. Add it to .gitignore.
2

Run Database Migrations

Before starting services, run migrations:
docker compose run --rm attendee-app python manage.py migrate
3

Collect Static Files

Collect Django static files:
docker compose run --rm attendee-app python manage.py collectstatic --noinput
4

Start Services

Launch all services in detached mode:
docker compose up -d
5

Create Superuser

Create an admin user:
docker compose exec attendee-app python manage.py createsuperuser

Health Checks

Monitor service health:
# Check running containers
docker compose ps

# View logs
docker compose logs -f attendee-app
docker compose logs -f attendee-worker

# Check Celery workers
docker compose exec attendee-worker celery -A attendee inspect active

Kubernetes Deployment

For production deployments at scale, Kubernetes provides orchestration and high availability.

Kubernetes Manifests

Namespace

namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: attendee

ConfigMap

configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: attendee-config
  namespace: attendee
data:
  POSTGRES_HOST: "postgres"
  REDIS_URL: "redis://redis:6379/0"
  DJANGO_SETTINGS_MODULE: "attendee.settings.production"
  ENABLE_CHROME_SANDBOX: "false"

Secret

# Create secret from .env file
kubectl create secret generic attendee-secrets \
  --from-literal=SECRET_KEY=your_secret_key \
  --from-literal=POSTGRES_PASSWORD=your_db_password \
  --from-literal=AWS_ACCESS_KEY_ID=your_aws_key \
  --from-literal=AWS_SECRET_ACCESS_KEY=your_aws_secret \
  -n attendee

Deployment

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: attendee-app
  namespace: attendee
spec:
  replicas: 3
  selector:
    matchLabels:
      app: attendee-app
  template:
    metadata:
      labels:
        app: attendee-app
    spec:
      containers:
      - name: attendee
        image: attendee:latest
        ports:
        - containerPort: 8000
        envFrom:
        - configMapRef:
            name: attendee-config
        - secretRef:
            name: attendee-secrets
        command: ["gunicorn"]
        args:
        - "attendee.wsgi:application"
        - "--bind"
        - "0.0.0.0:8000"
        - "--workers"
        - "4"
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "2Gi"
            cpu: "2000m"
        livenessProbe:
          httpGet:
            path: /health/
            port: 8000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health/
            port: 8000
          initialDelaySeconds: 10
          periodSeconds: 5

Worker Deployment

worker-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: attendee-worker
  namespace: attendee
spec:
  replicas: 2
  selector:
    matchLabels:
      app: attendee-worker
  template:
    metadata:
      labels:
        app: attendee-worker
    spec:
      containers:
      - name: worker
        image: attendee:latest
        envFrom:
        - configMapRef:
            name: attendee-config
        - secretRef:
            name: attendee-secrets
        command: ["celery"]
        args:
        - "-A"
        - "attendee"
        - "worker"
        - "-l"
        - "INFO"
        - "--concurrency=2"
        resources:
          requests:
            memory: "1Gi"
            cpu: "1000m"
          limits:
            memory: "4Gi"
            cpu: "4000m"

Service

service.yaml
apiVersion: v1
kind: Service
metadata:
  name: attendee-app
  namespace: attendee
spec:
  selector:
    app: attendee-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8000
  type: LoadBalancer
The Kubernetes dependency for managing resources is included in requirements.txt (kubernetes==32.0.0).

Reverse Proxy Setup

NGINX Configuration

Use NGINX as a reverse proxy:
nginx.conf
upstream attendee {
    server localhost:8000;
}

server {
    listen 80;
    server_name attendee.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name attendee.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/attendee.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/attendee.yourdomain.com/privkey.pem;

    client_max_body_size 100M;

    location / {
        proxy_pass http://attendee;
        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;
    }

    location /static/ {
        alias /var/www/attendee/staticfiles/;
    }

    location /ws/ {
        proxy_pass http://attendee;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}

Database Management

Backups

Automate PostgreSQL backups:
# Backup database
docker compose exec postgres pg_dump -U attendee_user attendee_production > backup_$(date +%Y%m%d_%H%M%S).sql

# Restore database
cat backup.sql | docker compose exec -T postgres psql -U attendee_user attendee_production

Migrations

Run migrations safely:
# Check migration status
docker compose exec attendee-app python manage.py showmigrations

# Run migrations
docker compose exec attendee-app python manage.py migrate

# Rollback migration (if needed)
docker compose exec attendee-app python manage.py migrate app_name migration_name

Monitoring & Logging

Log Aggregation

Configure structured logging:
# Enable JSON logging
JSON_LOGGING=true
LOG_LEVEL=INFO
View logs:
# Application logs
docker compose logs -f attendee-app

# Worker logs
docker compose logs -f attendee-worker

# Follow all logs
docker compose logs -f

Metrics & Health Checks

Monitor service health:
# Check application health
curl https://attendee.yourdomain.com/health/

# Check Celery worker status
docker compose exec attendee-worker celery -A attendee inspect stats

# Check Redis connection
docker compose exec redis redis-cli ping

Scaling Considerations

1

Horizontal Scaling

Scale workers based on load:
# Scale up workers
docker compose up -d --scale attendee-worker=5

# Kubernetes scaling
kubectl scale deployment attendee-worker --replicas=5 -n attendee
2

Database Performance

  • Use connection pooling (built-in with Django)
  • Consider read replicas for high read loads
  • Use managed database services (RDS, Cloud SQL) for automatic scaling
3

Redis Optimization

  • Use Redis persistence (AOF or RDB)
  • Consider Redis Cluster for high availability
  • Monitor memory usage and eviction policies
4

Media Storage

  • Use CDN for static files (CloudFront, Cloudflare)
  • Implement S3 lifecycle policies for old recordings
  • Consider multiple regions for global access

Security Hardening

Follow these security practices for production deployments:

Container Security

  • Run as non-root: Attendee already runs as app user (UID 1000)
  • Read-only filesystem: Mount volumes as read-only where possible
  • Resource limits: Set CPU and memory limits to prevent resource exhaustion
  • Security scanning: Regularly scan images for vulnerabilities

Network Security

# Docker compose network isolation
networks:
  attendee_network:
    driver: bridge
    internal: false
  • Use firewall rules to restrict access
  • Enable VPC/network policies in Kubernetes
  • Use TLS for all external communication

Secrets Management

  • Use secret management tools (AWS Secrets Manager, HashiCorp Vault)
  • Rotate credentials regularly
  • Never log sensitive information

Troubleshooting

Common Issues

Problem: Chrome crashes with “No usable sandbox” error.Solution: Disable Chrome sandbox in containers:
ENABLE_CHROME_SANDBOX=false
Alternatively, use the provided seccomp profile:
security_opt:
  - seccomp=./bots/web_bot_adapter/chrome_seccomp.json
Problem: Audio processing fails with PulseAudio errors.Solution: The entrypoint.sh script handles PulseAudio initialization. Verify:
  • XDG_RUNTIME_DIR is writable
  • User permissions are correct
Enable debug mode:
PA_DEBUG=1
Problem: Celery worker not picking up tasks.Solution: Check Redis connection and worker logs:
# Verify Redis connection
docker compose exec redis redis-cli ping

# Check worker status
docker compose exec attendee-worker celery -A attendee inspect active

# Check worker logs
docker compose logs attendee-worker
Problem: Cannot connect to PostgreSQL.Solution: Verify:
  • PostgreSQL is running: docker compose ps postgres
  • Credentials are correct in .env
  • Network connectivity: docker compose exec attendee-app ping postgres

Updates & Maintenance

Updating Attendee

1

Pull Latest Changes

git pull origin main
2

Rebuild Image

docker compose build
3

Stop Services

docker compose down
4

Run Migrations

docker compose run --rm attendee-app python manage.py migrate
5

Restart Services

docker compose up -d

Zero-Downtime Updates

For production, use rolling updates:
# Kubernetes rolling update
kubectl set image deployment/attendee-app attendee=attendee:v2.0 -n attendee
kubectl rollout status deployment/attendee-app -n attendee

# Rollback if needed
kubectl rollout undo deployment/attendee-app -n attendee

Next Steps

Setup

Return to setup guide

Configuration

Learn more about configuration options

API Reference

Explore the Attendee API

Community

Join the Slack community

Build docs developers (and LLMs) love