Production Deployment Guide

Optimal configurations for deploying your Django SaaS boilerplate on single servers of different sizes, targeting 80% resource utilization for maximum efficiency.

1. Architecture Overview

Based on the production Docker Compose configuration, the following services run on a single server:

  • Django/Gunicorn: Web application server
  • Celery Worker: Background task processing (optional)
  • Celery Beat: Scheduled task management (optional)
  • PostgreSQL: Primary database
  • Redis: Message broker and caching
  • Traefik: Reverse proxy and SSL termination
  • Flower: Celery monitoring (optional)

2. Server Configuration Matrix

Small Server (1 vCPU, 4GB RAM)

Suitable for development/staging or small applications (~100-1000 concurrent users)

Service Memory CPU Configuration
PostgreSQL 1.12GB (22.4% of total) 0.25 vCPU (20% of total) shared_buffers=280MB, max_connections=75
Django/Gunicorn 1.52GB (30.4% of total) 0.45 vCPU (36% of total) 6 sync workers
Celery Worker 720MB (14.4% of total) 0.15 vCPU (12% of total) concurrency=3, prefetch=1
Redis 240MB (4.8% of total) 0.05 vCPU (4% of total) maxmemory=180MB
System/Traefik 400MB (8% of total) 0.1 vCPU (8% of total) Basic reverse proxy
Total Usage 3.2GB (80% of 4GB) 0.8 vCPU (80% of 1 vCPU) Est. Users: 100-1000
WEB_CONCURRENCY=6
GUNICORN_CMD_ARGS="--max-requests=1000 --max-requests-jitter=50 --timeout=30"

CELERY_WORKER_CONCURRENCY=3
CELERY_WORKER_PREFETCH_MULTIPLIER=1

REDIS_MAXMEMORY=180mb

Medium Server (2 vCPU, 8GB RAM)

Suitable for small to medium applications (~1000-4000 concurrent users)

Service Memory CPU Configuration
PostgreSQL 2.24GB (22.4% of total) 0.5 vCPU (20% of total) shared_buffers=560MB, max_connections=150
Django/Gunicorn 3.04GB (30.4% of total) 0.9 vCPU (36% of total) WEB_CONCURRENCY=9 (gevent)
Celery Worker 1.44GB (14.4% of total) 0.3 vCPU (12% of total) concurrency=6, prefetch=2
Redis 480MB (4.8% of total) 0.1 vCPU (4% of total) maxmemory=360MB
System/Traefik 800MB (8% of total) 0.2 vCPU (8% of total) Enhanced monitoring
Total Usage 6.4GB (80% of 8GB) 1.6 vCPU (80% of 2 vCPU) Est. Users: 1000-4000
WEB_CONCURRENCY=9
GUNICORN_CMD_ARGS="--worker-class=gevent --worker-connections=1000 --max-requests=1200 --preload"

CELERY_WORKER_CONCURRENCY=6
CELERY_WORKER_PREFETCH_MULTIPLIER=2

REDIS_MAXMEMORY=360mb

Large Server (4 vCPU, 16GB RAM)

Suitable for medium to large applications (~4000-12000 concurrent users)

Service Memory CPU Configuration
PostgreSQL 4.48GB (22.4% of total) 1.0 vCPU (20% of total) shared_buffers=1120MB, max_connections=250
Django/Gunicorn 6.08GB (30.4% of total) 1.8 vCPU (36% of total) WEB_CONCURRENCY=17 (gevent)
Celery Worker 2.88GB (14.4% of total) 0.6 vCPU (12% of total) concurrency=10, prefetch=3
Redis 960MB (4.8% of total) 0.2 vCPU (4% of total) maxmemory=720MB
System/Traefik 1.6GB (8% of total) 0.4 vCPU (8% of total) Full monitoring stack
Total Usage 12.8GB (80% of 16GB) 3.2 vCPU (80% of 4 vCPU) Est. Users: 4000-12000
WEB_CONCURRENCY=17
GUNICORN_CMD_ARGS="--worker-class=gevent --worker-connections=1200 --max-requests=1500 --preload"

CELERY_WORKER_CONCURRENCY=10
CELERY_WORKER_PREFETCH_MULTIPLIER=3

REDIS_MAXMEMORY=720mb

Additional Configurations: The guide also includes configurations for Extra Large (8 vCPU, 32GB RAM) and Enterprise (16 vCPU, 64GB RAM) servers, supporting up to 25,000+ concurrent users.

3. Implementation Guide

Update Docker Compose Configuration

Tweak resource limits to your docker-compose.production.yml:

Environment Configuration

Update your .envs/production/django.env file based on the server size.

4. Performance Optimization Tips

Application Optimization

  • • Implement Redis caching for frequently accessed data
  • • Use CDN for static file delivery in production
  • • Consider database sessions for better scalability
  • • Ensure proper indexing for frequently queried fields
  • • Use select_related() and prefetch_related() in your django application to prevent N+1 query problems

Connection Pooling Configuration

Django includes built-in connection pooling via the CONN_MAX_AGE setting:

Server Size CONN_MAX_AGE Max Connections Rationale
1 vCPU, 4GB 60 seconds 75 Balanced connection reuse
2 vCPU, 8GB 120 seconds 150 Longer reuse for performance
4 vCPU, 16GB 180 seconds 250 Extended reuse with more memory
8 vCPU, 32GB 300 seconds 400 Maximum connection reuse
16 vCPU, 64GB 300 seconds 600 Maximum reuse with enterprise capacity

5. Monitoring & Alerts

Error Monitoring with Sentry

Sentry must be connected for production error monitoring and debugging. Without proper error tracking, identifying and fixing issues becomes extremely difficult in production environments.

To set up Sentry error monitoring:

  1. Visit sentry.io and create an account or log in
  2. Create a new project and select "Django" as the platform
  3. Copy the provided SENTRY_DSN URL from your project settings
  4. Add the DSN to your environment file: .envs/production/django.env
SENTRY_DSN=https://[email protected]/your-project-id

After adding the DSN, restart your application containers to enable error tracking and performance monitoring.

6. Scaling Considerations

Vertical Scaling Limits

Single server setup is optimal up to 16 vCPU / 64GB RAM. Beyond this point, consider horizontal scaling strategies.

Horizontal Scaling Transition

When you outgrow single-server deployments:

Database Scaling

  • • Separate database server
  • • Read replicas
  • • Connection pooling (PgBouncer)

Application Scaling

  • • Multiple app servers
  • • Load balancer
  • • Shared Redis cluster

Storage Scaling

  • • External file storage (S3 or Google Cloud Storage)
  • • CDN for static assets
  • • Distributed sessions

7. Troubleshooting Common Issues

High Memory Usage

  • • Check for memory leaks in Django application
  • • Verify Gunicorn worker memory limits
  • • Monitor PostgreSQL shared_buffers allocation
  • • Review Redis memory usage patterns

High CPU Usage

  • • Check for inefficient database queries
  • • Monitor Celery task processing bottlenecks
  • • Verify proper worker concurrency settings
  • • Review application code for CPU-intensive operations

Database Connection Issues

  • • Monitor PostgreSQL max_connections setting
  • • Check for connection pool exhaustion
  • • Verify proper connection closing in application code

Task Queue Backlog

  • • Increase Celery worker concurrency
  • • Monitor task execution times
  • • Check for failing tasks causing retries
  • • Consider scaling Celery workers horizontally

8. Frontend Assets & Tailwind Build

Before deploying to production, you must build and optimize your CSS assets for optimal performance. This section covers the Tailwind CSS build process and how to reference the minified files in your templates.

Building Minified Tailwind CSS

The project uses Tailwind CSS for styling. During development, you use the unminified output.css file, but for production, you should build a minified version for better performance.

Building Production CSS

Navigate to the CSS utilities directory and run the build command:

cd project_name/static/jsutils npm run tailwind:build

This command compiles and minifies your Tailwind CSS from tailwind.css to ../css/dist.css

Automatic Environment-Specific CSS Loading

The base.html template is already configured to automatically load the appropriate CSS file based on your environment:

{# Load different CSS files based on environment:
- output.css for development (unminified, includes all styles)
- dist.css for production (minified, optimized for performance) #}

{% if debug %}
    <link href="{% static 'css/output.css' %}" rel="stylesheet" />
{% else %}
    <link href="{% static 'css/dist.css' %}" rel="stylesheet" />
{% endif %}

Deployment Checklist

Before deploying to production:

Ready to launch!

Congratulations! You are now ready to launch your SaaS application. Of course it does not end here; you still need to market it. But no worries, your competition is still trying to set up the boring stuff.

Good luck! 🚀

Need Help?

If you encounter any issues during deployment or have questions about the configuration, I'm here to help!

I typically respond within 24 hours. Include your server specifications and any error messages for faster troubleshooting.