Part 7: Deployment — From localhost:3000 to production.yoursite.com
In Part 1, we containerized everything. In Part 2, we built the database. In Part 3, we cached the API. In Part 4, we optimized queries. In Part 5, we built the UI. In Part 6, we integrated the CDN. Today, we deploy to production.
If you're jumping in here, you need the full context. Part 1 through Part 6 built a complete, optimized housing portal that runs on localhost. If you're continuing from Part 6, you have a working app with images, caching, and all the features — but only you can access it.
Today we make it public. By the end of this post, you'll understand deployment architectures, choose the right hosting strategy for your needs, and have a live site accessible from anywhere. We'll cover four complete deployment paths — from completely free (for demos) to enterprise-grade AWS infrastructure.
The Deployment Challenge
Deploying a full-stack application isn't like deploying a simple website. We have:
- A Next.js frontend — needs Node.js, build process, edge caching
- A Django backend — needs Python, Gunicorn, environment variables
- A PostgreSQL database — needs persistence, backups, connection pooling
- A Redis cache — needs memory, persistence configuration
- A Cloudinary CDN — already deployed, just needs credentials
- Static files — CSS, JS, images that need to be served efficiently
- Environment variables — different for dev, staging, production
- SSL certificates — HTTPS is mandatory for production
- Domain names — custom domains or free subdomains
There isn't one "right" way to deploy all of this. The right choice depends on your budget, traffic, technical comfort, and requirements.
This post shows you four complete deployment architectures and helps you choose:
Part A: The Architecture Decision Tree
Before we deploy anything, we need to understand the options.
The Four Deployment Paths
Path 0: The Free Tier (Demo/Portfolio)
- Cost: $0/month
- Complexity: Low-Medium
- Best for: This blog series, demos, portfolio projects, MVPs
- Tradeoff: Cold starts (30s delay after inactivity), usage limits
Path 1: Railway Only (Simple)
- Cost: ~$20/month
- Complexity: Low
- Best for: Side projects, MVPs, small apps
- Tradeoff: Higher cost at scale, less control
Path 2: Vercel + Railway (Optimal)
- Cost: ~$15-35/month
- Complexity: Medium
- Best for: Production apps, startups, SaaS
- Tradeoff: More moving parts to manage
Path 3: AWS DIY (Enterprise)
- Cost: ~$90+/month
- Complexity: High
- Best for: Enterprise, compliance needs, learning DevOps
- Tradeoff: Requires infrastructure knowledge
Decision Criteria
Choose Path 0 (Free Tier) if:
- ✅ Following this blog series and don't want to spend money
- ✅ Building a portfolio project or demo
- ✅ Testing an MVP with < 10 daily users
- ✅ Cold starts (30s first load) are acceptable
- ✅ You plan to upgrade when you get traction
Choose Path 1 (Railway Only) if:
- ✅ Want the simplest deployment possible
- ✅ Have a small budget ($20/month)
- ✅ Don't want to manage infrastructure
- ✅ Need automatic SSL and deployments
- ✅ Okay with vendor lock-in
Choose Path 2 (Vercel + Railway) if:
- ✅ Need production-grade performance
- ✅ Want global edge caching for frontend
- ✅ Can manage multiple services
- ✅ Need 99.9% uptime
- ✅ This is a real product with users
Choose Path 3 (AWS DIY) if:
- ✅ Need full control of infrastructure
- ✅ Have compliance requirements (HIPAA, SOC2)
- ✅ Want to learn DevOps
- ✅ Have high traffic (100k+ requests/day)
- ✅ Can manage servers
📊 [Architecture Decision Tree]
Architecture Patterns Comparison
Let's visualize each architecture before we dive into implementation.
Pattern 1: The Monolith (Railway Only)
Everything runs on one platform. Simple but less optimized.
User → Railway (Frontend + Backend + DB + Redis)
📊 [Monolith Architecture]
Pattern 2: The Modern Split (Vercel + Railway)
Frontend on edge (Vercel), backend on PaaS (Railway). Optimal performance.
User → Vercel Edge (Next.js) → Railway (Django + DB + Redis)
📊 [Modern Split Architecture]
Pattern 3: The Cloud DIY (AWS)
Self-managed infrastructure. Maximum control, maximum complexity.
User → CloudFront → ALB → EC2 (Django) → RDS (PostgreSQL)
→ ElastiCache (Redis)
📊 [AWS Architecture]
Cost vs Complexity Matrix
| Deployment Path | Monthly Cost | Setup Time | Maintenance | Performance | Scalability |
|---|---|---|---|---|---|
| Path 0: Free Tier | $0 | 2 hours | None | 3/5 ⭐ | Limited |
| Path 1: Railway | $20 | 1 hour | Minimal | 4/5 ⭐ | Medium |
| Path 2: Vercel+Railway | $15-35 | 3 hours | Low | 5/5 ⭐ | High |
| Path 3: AWS DIY | $90+ | 8 hours | High | 5/5 ⭐ | Very High |
For this tutorial series, Path 0 (Free Tier) is perfect for learning. When you're ready to launch, Path 2 (Vercel + Railway) is the sweet spot.
Part B: Production Readiness — Preparing the Code
Before we deploy anywhere, we need to prepare our code for production. The settings that work in development are not safe for production.
Step 1: Create Production Django Settings
We'll use a split settings pattern: settings/base.py, settings/development.py, settings/production.py.
Create backend/core/settings/ directory:
mkdir -p backend/core/settings
Move existing settings to base:
mv backend/core/settings.py backend/core/settings/base.py
Create backend/core/settings/__init__.py:
"""
core/settings/__init__.py
Import the correct settings based on DJANGO_ENV environment variable.
Defaults to development if not set.
"""
import os
env = os.environ.get('DJANGO_ENV', 'development')
if env == 'production':
from .production import *
elif env == 'staging':
from .staging import *
else:
from .development import *
Create backend/core/settings/development.py:
"""
core/settings/development.py
Development-specific settings.
Imports from base and overrides as needed.
"""
from .base import *
DEBUG = True
ALLOWED_HOSTS = ['*']
# Use console email backend in development
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# Show SQL queries in debug toolbar
DEBUG_TOOLBAR_CONFIG = {
'SHOW_TOOLBAR_CALLBACK': lambda request: DEBUG,
}
Create backend/core/settings/production.py:
"""
core/settings/production.py
Production settings with security hardening.
"""
from .base import *
import os
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
# Must be set in environment variables
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')
CSRF_TRUSTED_ORIGINS = os.environ.get('CSRF_TRUSTED_ORIGINS', '').split(',')
# Security settings
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
X_FRAME_OPTIONS = 'DENY'
SECURE_HSTS_SECONDS = 31536000 # 1 year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
# Use a strong secret key from environment
SECRET_KEY = os.environ.get('SECRET_KEY')
if not SECRET_KEY:
raise ValueError('SECRET_KEY environment variable must be set in production')
# Database connection pooling
DATABASES['default']['CONN_MAX_AGE'] = 600 # 10 minutes
# Static files (WhiteNoise)
MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# CORS settings for production
CORS_ALLOWED_ORIGINS = os.environ.get('CORS_ALLOWED_ORIGINS', '').split(',')
# Logging configuration
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {message}',
'style': '{',
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
},
'root': {
'handlers': ['console'],
'level': 'INFO',
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'INFO',
'propagate': False,
},
},
}
Install WhiteNoise for static file serving:
cd backend
pip install whitenoise
pip freeze > requirements.txt
Step 2: Create Next.js Environment Files
Create frontend/.env.production:
# Production environment variables
# Update these with your actual production URLs
NEXT_PUBLIC_API_URL=https://your-backend.railway.app
# or
NEXT_PUBLIC_API_URL=https://api.yourdomain.com
Create frontend/.env.local.example:
# Example environment variables
# Copy this to .env.local and fill in your values
NEXT_PUBLIC_API_URL=http://localhost:8000
Update frontend/lib/api.ts to use environment variables:
/**
* lib/api.ts
*
* Updated to use environment variables for production.
*/
const getBaseURL = () => {
// In production, use the environment variable
if (process.env.NEXT_PUBLIC_API_URL) {
return process.env.NEXT_PUBLIC_API_URL;
}
// Development: differentiate server vs client
if (typeof window === 'undefined') {
return 'http://backend:8000';
}
return 'http://localhost:8000';
};
export const API_BASE_URL = getBaseURL();
// ... rest of the file stays the same
Step 3: Update Dependencies
Create backend/requirements/base.txt:
Django==5.0.1
djangorestframework==3.14.0
django-cors-headers==4.3.1
django-filter==23.5
psycopg2-binary==2.9.9
redis==5.0.1
django-redis==5.4.0
cloudinary==1.37.0
django-cloudinary-storage==0.3.0
gunicorn==21.2.0
whitenoise==6.6.0
Create backend/requirements/production.txt:
-r base.txt
# Production-specific packages
sentry-sdk==1.39.2
Create backend/requirements/development.txt:
-r base.txt
# Development-specific packages
django-debug-toolbar==4.2.0
Update backend/requirements.txt:
-r requirements/production.txt
Step 4: Create .gitignore
Ensure these are in .gitignore:
# Environment variables
.env
.env.local
.env.production
.env.staging
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
venv/
ENV/
# Django
*.log
db.sqlite3
staticfiles/
mediafiles/
# Next.js
.next/
out/
node_modules/
# IDEs
.vscode/
.idea/
*.swp
*.swo
Step 5: Generate Production Secrets
Generate a secure SECRET_KEY for Django:
python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'
Save this — you'll need it in environment variables.
Part C: Path 0 — The Free Tier Deployment (RECOMMENDED FOR THIS TUTORIAL)
This is the perfect path for following this blog series without spending money. We'll use 100% free tiers.
What we're deploying:
- Frontend: Vercel (free tier)
- Backend: Render.com (free tier)
- Database: Neon.tech (free PostgreSQL, 0.5GB)
- Redis: Upstash (free tier, 10k requests/day)
- Images: Cloudinary (already set up, free tier)
Total cost: $0/month
Tradeoffs to accept:
- Backend sleeps after 15 minutes of inactivity (30-second cold start on first request)
- Database limited to 0.5GB (~50,000 property records)
- Redis limited to 10,000 requests per day
- Free subdomains only (no custom domain)
This is perfect for demos, portfolios, and learning deployments.
Step 1: Deploy Database (Neon.tech)
- Go to neon.tech
- Sign up with GitHub (no credit card required)
- Click "Create a project"
- Name:
housing-portal-db - Region: Choose closest to you (us-east-1, eu-central-1, etc.)
- Click "Create project"
You'll get a connection string like:
postgres://username:password@ep-long-string.us-east-1.aws.neon.tech/neondb?sslmode=require
Save this — you'll need it for the backend.
Neon automatically handles:
- ✅ SSL connections
- ✅ Automatic backups
- ✅ Connection pooling
- ✅ Scales to zero (no usage = no resources used)
Step 2: Deploy Redis (Upstash)
- Go to upstash.com
- Sign up with GitHub
- Click "Create Database"
- Name:
housing-portal-redis - Type: Regional
- Region: Choose same as Neon (for lower latency)
- Eviction: No eviction
- Click "Create"
You'll get a connection string like:
redis://default:password@us1-host.upstash.io:port
Or REST URL (alternative if Redis protocol doesn't work):
https://us1-host.upstash.io
Save this connection string.
Step 3: Deploy Backend (Render.com)
- Go to render.com
- Sign up with GitHub
- Click "New +" → "Web Service"
- Connect your GitHub repository
- Select the
housing-caching-demorepository - Configure:
Settings:
- Name:
housing-portal-backend - Region: Same as database
- Branch:
main - Root Directory:
backend - Runtime:
Python 3 - Build Command:
pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate - Start Command:
gunicorn core.wsgi:application --bind 0.0.0.0:$PORT - Instance Type: Free
Environment Variables:
Click "Add Environment Variable" for each:
DJANGO_ENV=production
SECRET_KEY=<your-generated-secret-key>
DEBUG=False
ALLOWED_HOSTS=<will-fill-after-deploy>.onrender.com
CSRF_TRUSTED_ORIGINS=https://<will-fill-after-deploy>.onrender.com
DATABASE_URL=<neon-postgres-url>
REDIS_URL=<upstash-redis-url>
CLOUDINARY_CLOUD_NAME=<your-cloudinary-name>
CLOUDINARY_API_KEY=<your-cloudinary-key>
CLOUDINARY_API_SECRET=<your-cloudinary-secret>
CORS_ALLOWED_ORIGINS=https://your-app.vercel.app
Note: You'll update ALLOWED_HOSTS and CORS_ALLOWED_ORIGINS after you know the actual URLs.
- Click "Create Web Service"
Render will:
- Build your app (takes 2-3 minutes)
- Run migrations
- Start the server
You'll get a URL like: https://housing-portal-backend.onrender.com
Test it:
curl https://housing-portal-backend.onrender.com/api/properties/cached/
If you get JSON back, it works! If you get a 403 or CORS error, that's expected — we haven't configured CORS yet.
Now go back and update environment variables:
ALLOWED_HOSTS=housing-portal-backend.onrender.com
CSRF_TRUSTED_ORIGINS=https://housing-portal-backend.onrender.com
Step 4: Deploy Frontend (Vercel)
- Go to vercel.com
- Sign up with GitHub
- Click "Add New..." → "Project"
- Import your
housing-caching-demorepository - Configure:
Settings:
- Framework Preset: Next.js
- Root Directory:
frontend - Build Command:
npm run build(default) - Output Directory:
.next(default)
Environment Variables:
Click "Add" for each environment:
NEXT_PUBLIC_API_URL=https://housing-portal-backend.onrender.com
- Click "Deploy"
Vercel will:
- Install dependencies
- Build the Next.js app
- Deploy to edge network globally
You'll get a URL like: https://housing-portal-abc123.vercel.app
Test it:
Open that URL in your browser. You should see your housing portal!
Step 5: Connect Frontend to Backend (CORS)
Now update the backend CORS settings to allow requests from Vercel:
Go back to Render.com → your backend service → Environment → Edit CORS_ALLOWED_ORIGINS:
CORS_ALLOWED_ORIGINS=https://housing-portal-abc123.vercel.app
Click "Save Changes" → Render will redeploy.
Test again:
Refresh your Vercel URL. The property listings should load.
Step 6: Understanding Cold Starts
The First Request:
Since Render's free tier sleeps after 15 minutes of inactivity, the first request will:
- Wake up the server (15-30 seconds)
- Connect to database
- Return response
Subsequent Requests:
Instant (as long as you keep using it within 15 minutes).
How to explain this to users:
Add a note in your README or on the site:
"This is a demo deployed on free hosting. The first load may take 30 seconds as the server wakes up. Subsequent loads are instant."
Step 7: Verifying the Deployment
Check each component:
-
Frontend:
https://your-app.vercel.app— should load the homepage -
Backend API:
https://your-backend.onrender.com/api/properties/cached/— should return JSON - Database: Properties should display (proves DB connection works)
- Redis: Navigate between pages quickly (proves cache works)
- Images: Property cards should show images (proves Cloudinary works)
Open DevTools and check:
- Network tab → No CORS errors
- No console errors
- Images load from
res.cloudinary.com
📊 [Exercise / Showcase Free Tier Deployment - Live Site]
Instructions: Capture a screenshot of your deployed site on Vercel showing property listings loading successfully.
Step 8: Free Tier Limits and Monitoring
Neon.tech:
- 0.5GB storage (check usage in dashboard)
- Unlimited queries
- Auto-scales to zero (saves resources when not in use)
Upstash:
- 10,000 requests per day
- Dashboard shows usage
- Resets daily
Render.com:
- 750 hours/month (31 days = 744 hours — enough for 1 service)
- 512MB RAM
- Sleeps after 15 min inactivity
Vercel:
- Unlimited bandwidth (fair use)
- Unlimited deployments
- 100GB bandwidth/month (more than enough)
Cloudinary:
- 25GB storage
- 25GB bandwidth/month
- Unlimited transformations
When you'll hit limits:
- Upstash: ~400 users per day (assuming 25 requests each)
- Neon: ~50,000 properties (way more than needed for demo)
- Never hitting Vercel/Cloudinary limits for a demo
Step 9: Migration Path (When to Upgrade)
Free Tier is fine when:
- < 50 daily active users
- Demo/portfolio project
- MVP validation
- Cold starts are acceptable
Upgrade to Railway ($5/month) when:
- 50-500 daily active users
- Can't tolerate cold starts
- Need custom domain
- Exceed Redis 10k requests/day
Upgrade to Vercel + Railway ($15-35/month) when:
- 500+ daily active users
- Need 99.9% uptime
- Global audience (Vercel edge network)
- This is a real business
Step 10: Adding a Custom Domain (Optional)
Even on free tiers, you can add a custom domain if you have one.
Vercel:
- Go to your project → Settings → Domains
- Add your domain (e.g.,
housing.yourdomain.com) - Update DNS:
- Type: CNAME
- Name:
housing - Value:
cname.vercel-dns.com
- Vercel automatically provisions SSL
Render:
- Go to your service → Settings → Custom Domains
- Add
api.yourdomain.com - Update DNS:
- Type: CNAME
- Name:
api - Value:
your-service.onrender.com
- Render automatically provisions SSL
Update environment variables:
- Render:
ALLOWED_HOSTS=api.yourdomain.com - Render:
CSRF_TRUSTED_ORIGINS=https://api.yourdomain.com - Vercel: (no change needed)
Path 0 Complete! You now have a fully deployed housing portal for $0/month. This is perfect for following the tutorial, building your portfolio, or validating an MVP.
Part D: Path 1 — Railway Only (Simple)
If you need to avoid cold starts and want everything in one place, Railway is the simplest paid option.
What we're deploying:
- Frontend: Railway
- Backend: Railway
- Database: Railway PostgreSQL
- Redis: Railway Redis
Cost: ~$20/month ($5/service × 4 services)
Pros:
- No cold starts
- Everything in one dashboard
- Automatic SSL
- Easy scaling
- Preview deployments
Step 1: Create Railway Account
- Go to railway.app
- Sign up with GitHub
- Click "New Project"
- Select "Deploy from GitHub repo"
- Choose
housing-caching-demo
Step 2: Add Services
Railway will create one service by default. We need 4 total.
Add PostgreSQL:
- Click "New" → "Database" → "Add PostgreSQL"
- Railway provisions a database automatically
- Connection string appears in the service variables
Add Redis:
- Click "New" → "Database" → "Add Redis"
- Railway provisions Redis automatically
Add Backend Service:
- Click "New" → "GitHub Repo" → Select your repo
- Configure:
- Root Directory:
backend - Build Command:
pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate - Start Command:
gunicorn core.wsgi:application --bind 0.0.0.0:$PORT
- Root Directory:
Add Frontend Service:
- Click "New" → "GitHub Repo" → Select same repo
- Configure:
- Root Directory:
frontend - Build Command:
npm install && npm run build - Start Command:
npm start
- Root Directory:
Step 3: Configure Environment Variables
Backend Service:
Click on backend → Variables → Add Reference Variables:
Railway automatically creates DATABASE_URL and REDIS_URL when you add those services.
Add these manually:
DJANGO_ENV=production
SECRET_KEY=<your-secret-key>
DEBUG=False
ALLOWED_HOSTS=${{RAILWAY_PUBLIC_DOMAIN}}
CSRF_TRUSTED_ORIGINS=https://${{RAILWAY_PUBLIC_DOMAIN}}
CLOUDINARY_CLOUD_NAME=<your-value>
CLOUDINARY_API_KEY=<your-value>
CLOUDINARY_API_SECRET=<your-value>
CORS_ALLOWED_ORIGINS=https://${{frontend.RAILWAY_PUBLIC_DOMAIN}}
Frontend Service:
NEXT_PUBLIC_API_URL=https://${{backend.RAILWAY_PUBLIC_DOMAIN}}
The ${{}} syntax is Railway's variable reference system. It automatically resolves to the actual domain of each service.
Step 4: Generate Public Domains
Each service gets a Railway domain automatically:
- Click backend service → Settings → Public Networking → Generate Domain
- Click frontend service → Settings → Public Networking → Generate Domain
You'll get:
- Backend:
https://housing-backend-production-abc.up.railway.app - Frontend:
https://housing-frontend-production-xyz.up.railway.app
Step 5: Deploy
Railway automatically deploys when you:
- Push to GitHub
- Change environment variables
- Update settings
Watch the deployment logs in each service.
Test:
Open the frontend URL. Everything should work!
Step 6: Custom Domain (Optional)
Add custom domain to frontend:
- Frontend service → Settings → Custom Domains
- Add
housing.yourdomain.com - Update DNS (CNAME to Railway domain)
- Railway provisions SSL automatically
Add custom domain to backend:
- Backend service → Settings → Custom Domains
- Add
api.yourdomain.com - Update DNS
- Update
CORS_ALLOWED_ORIGINSto include new domain
Step 7: Railway Pricing
Railway charges $5/month per service that uses resources.
Free tier:
- $5 free credit/month
- Shared resources
- Good for one service
Hobby plan:
- $5/service/month
- Dedicated resources
- No sleep
- Automatic scaling
For our 4 services:
- PostgreSQL: $5/month
- Redis: $5/month
- Backend: $5/month
- Frontend: $5/month Total: $20/month
Part E: Path 2 — Vercel + Railway (RECOMMENDED FOR PRODUCTION)
This is the optimal architecture for production apps. Frontend on Vercel's edge network (global CDN), backend on Railway (simple PaaS).
What we're deploying:
- Frontend: Vercel (edge network, 100+ locations globally)
- Backend: Railway
- Database: Railway PostgreSQL
- Redis: Railway Redis or Upstash
Cost: $15-35/month
- Vercel: Free (hobby) or $20/month (pro)
- Railway: $15/month (3 services: backend, DB, Redis)
Why this is optimal:
- ✅ Frontend served from edge (lowest latency globally)
- ✅ Backend centralized (simpler to manage)
- ✅ Best performance-to-cost ratio
- ✅ Each component runs on its optimal platform
Step 1: Deploy Backend to Railway
Follow the same steps as Path 1 for:
- PostgreSQL service
- Redis service
- Backend service
Environment variables:
DJANGO_ENV=production
SECRET_KEY=<your-secret>
ALLOWED_HOSTS=${{RAILWAY_PUBLIC_DOMAIN}}
CSRF_TRUSTED_ORIGINS=https://${{RAILWAY_PUBLIC_DOMAIN}}
DATABASE_URL=${{Postgres.DATABASE_URL}}
REDIS_URL=${{Redis.REDIS_URL}}
CLOUDINARY_CLOUD_NAME=<your-value>
CLOUDINARY_API_KEY=<your-value>
CLOUDINARY_API_SECRET=<your-value>
CORS_ALLOWED_ORIGINS=https://your-app.vercel.app
Generate domain for backend.
You'll get: https://housing-backend-production.up.railway.app
Step 2: Deploy Frontend to Vercel
- Go to vercel.com
- Import project from GitHub
-
Configure:
- Root Directory:
frontend - Framework: Next.js
- Environment Variable:
NEXT_PUBLIC_API_URL=https://housing-backend-production.up.railway.app - Root Directory:
Deploy
You'll get: https://housing-portal.vercel.app
Step 3: Update CORS
Go back to Railway → Backend → Environment Variables → Update:
CORS_ALLOWED_ORIGINS=https://housing-portal.vercel.app
Save and redeploy.
Step 4: Test the Integration
Open https://housing-portal.vercel.app:
- First load: Served from Vercel edge (fast globally)
- API calls: Go to Railway backend
- Images: Served from Cloudinary CDN
- Static files: Served from Vercel edge
Check DevTools:
- Network tab → API calls to
housing-backend-production.up.railway.app - Images from
res.cloudinary.com - HTML/CSS/JS from Vercel edge
Step 5: Add Custom Domains
Frontend:
- Vercel → Project → Settings → Domains
- Add
housing.yourdomain.comandwww.housing.yourdomain.com - Update DNS:
CNAME housing cname.vercel-dns.com
CNAME www cname.vercel-dns.com
- Vercel provisions SSL (automatic)
Backend:
- Railway → Backend → Settings → Custom Domains
- Add
api.yourdomain.com - Update DNS:
CNAME api housing-backend-production.up.railway.app
- Railway provisions SSL (automatic)
Update environment variables:
- Railway:
ALLOWED_HOSTS=api.yourdomain.com - Railway:
CORS_ALLOWED_ORIGINS=https://housing.yourdomain.com,https://www.housing.yourdomain.com - Vercel:
NEXT_PUBLIC_API_URL=https://api.yourdomain.com
Step 6: Performance Optimization
Vercel Edge Caching:
Next.js automatically caches static assets on Vercel's edge network. No configuration needed.
SWR Client Caching:
Already implemented in Part 5. Works automatically.
Railway Connection Pooling:
Railway handles this automatically.
Result:
- US user: ~50ms TTFB
- EU user: ~80ms TTFB
- Asia user: ~150ms TTFB
Much better than a single-region deployment!
📊 [Exercise: Vercel Deployment Dashboard]
Instructions: Capture screenshot of Vercel deployment success page showing build logs and deployment URL.
Part F: Path 3 — AWS DIY (Advanced)
This is for readers who want full control or need to learn infrastructure.
What we're deploying:
- Frontend: Vercel (or AWS Amplify if you want 100% AWS)
- Backend: EC2 instance with Nginx + Gunicorn
- Database: RDS PostgreSQL
- Redis: ElastiCache
- Load Balancer: Application Load Balancer
- SSL: AWS Certificate Manager
- Static files: S3 + CloudFront (optional, or use WhiteNoise)
Cost: ~$90/month
- EC2 t3.small: $15/month
- RDS db.t3.micro: $15/month
- ElastiCache cache.t3.micro: $12/month
- ALB: $16/month
- S3/CloudFront: $5/month
- Data transfer: $10-20/month
Complexity: High
This section provides the architecture and key steps. Full AWS deployment would be a separate multi-part series.
AWS Architecture Overview
Internet
↓
Route 53 (DNS)
↓
CloudFront (optional, for static files)
↓
Application Load Balancer
↓
EC2 Instance (Django + Gunicorn + Nginx)
↓
├─ RDS PostgreSQL (VPC, private subnet)
└─ ElastiCache Redis (VPC, private subnet)
High-Level Steps (Not Complete Tutorial)
Due to complexity, this is an overview. For full AWS deployment, refer to the Django Deployment Guide by SaaS Pegasus.
1. Provision Infrastructure:
- Create VPC with public/private subnets
- Launch RDS PostgreSQL in private subnet
- Launch ElastiCache Redis in private subnet
- Launch EC2 instance in public subnet
- Configure security groups (allow port 80, 443, 22)
2. Set Up EC2 Instance:
SSH into instance:
ssh -i your-key.pem ubuntu@ec2-instance-ip
Install dependencies:
sudo apt update
sudo apt install python3-pip python3-venv nginx postgresql-client redis-tools
Clone your repository:
git clone https://github.com/yourusername/housing-caching-demo.git
cd housing-caching-demo/backend
Create virtual environment:
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
3. Configure Gunicorn:
Create /etc/systemd/system/gunicorn.service:
4. Configure Nginx:
Create /etc/nginx/sites-available/housing-portal:
📄 [GIST 1: nginx.conf]
Gist URL:
https://gist.github.com/urwithajit9/11b25980f75649279fec917f05cb5565
5. SSL with Let's Encrypt:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d api.yourdomain.com
Certbot automatically updates Nginx config for HTTPS.
6. Set Up Application:
Set environment variables in /home/ubuntu/.env:
DJANGO_ENV=production
SECRET_KEY=...
DATABASE_URL=postgres://user:pass@rds-endpoint:5432/housing_db
REDIS_URL=redis://elasticache-endpoint:6379
...
Run migrations:
python manage.py migrate
python manage.py collectstatic --noinput
Start services:
sudo systemctl start gunicorn
sudo systemctl enable gunicorn
sudo systemctl restart nginx
7. Deploy Frontend:
Either:
- Deploy to Vercel (same as Path 2)
- Or deploy to AWS Amplify
- Or serve from same EC2 with Nginx
8. Set Up Load Balancer (Optional but Recommended):
- Create Application Load Balancer
- Point to EC2 instance(s)
- Configure health checks
- Update Route 53 to point to ALB instead of EC2 directly
9. Auto Scaling (Optional):
- Create Auto Scaling Group
- Define scaling policies
- ALB distributes traffic across instances
When to Use AWS DIY
Use AWS when:
- You need full control (compliance, security requirements)
- You're learning DevOps
- You have high traffic (100k+ requests/day)
- You're already on AWS ecosystem
- You need custom infrastructure (VPN, peering, etc.)
Don't use AWS when:
- You're building a side project (overkill)
- You don't have DevOps experience
- You want to focus on features, not infrastructure
- You're a solo developer (too much to manage)
AWS Cost Optimization Tips
- Use Reserved Instances: Save 30-40% by committing to 1 year
- Right-size instances: Start with t3.micro, scale up only when needed
- Use S3 for static files: Cheaper than serving from EC2
- Enable CloudWatch alarms: Catch runaway costs early
- Use AWS Free Tier: RDS and EC2 have free tiers for 12 months
Part G: Domain and SSL Configuration
Regardless of which path you chose, you'll want custom domains and HTTPS.
Step 1: Register a Domain
Popular registrars:
- Namecheap: ~$10/year for .com
- Google Domains: ~$12/year
- Cloudflare Registrar: At-cost pricing (~$9/year)
- Porkbun: Budget option (~$8/year)
For this tutorial, we'll use housing-demo.com as an example.
Step 2: DNS Configuration
You need to point your domain to your deployed services.
For Vercel (Frontend):
Type: CNAME
Name: housing (or www or @)
Value: cname.vercel-dns.com
TTL: 3600
For Railway (Backend):
Type: CNAME
Name: api
Value: your-service.up.railway.app
TTL: 3600
For Render (Backend):
Type: CNAME
Name: api
Value: your-service.onrender.com
TTL: 3600
For AWS (Backend with ALB):
Type: A (Alias)
Name: api
Value: your-alb.us-east-1.elb.amazonaws.com
TTL: 3600
Step 3: SSL Certificates
Vercel: Automatic. Provisions SSL within minutes of adding domain.
Railway: Automatic. Provisions SSL via Let's Encrypt.
Render: Automatic. Provisions SSL via Let's Encrypt.
AWS: Use AWS Certificate Manager:
- Request certificate for
api.yourdomain.com - Verify via email or DNS
- Attach certificate to ALB
Self-managed (VPS): Use Certbot:
sudo certbot --nginx -d api.yourdomain.com
Step 4: Force HTTPS
Django (already configured in production.py):
SECURE_SSL_REDIRECT = True
Nginx (if using):
server {
listen 80;
server_name api.yourdomain.com;
return 301 https://$server_name$request_uri;
}
Next.js: Handled by platform (Vercel/Railway).
Step 5: DNS Propagation
After updating DNS, changes take time to propagate:
- Minimum: 5 minutes
- Typical: 1-2 hours
- Maximum: 48 hours (rare)
Check propagation:
dig housing-demo.com
dig api.housing-demo.com
Or use online tool: dnschecker.org
Step 6: Test HTTPS
curl -I https://housing-demo.com
# Should show: HTTP/2 200
# Should NOT redirect to HTTP
curl -I https://api.housing-demo.com/api/properties/cached/
# Should show: HTTP/2 200
# Should return JSON
Part H: CI/CD Pipeline with GitHub Actions
Automate deployments so every push to main deploys automatically.
Deployment Flow
Developer pushes to GitHub
↓
GitHub Actions triggers
↓
Run tests
↓
Deploy to Vercel (frontend)
↓
Deploy to Railway/Render (backend)
↓
Run migrations
↓
Send notification (Slack/email)
📊 [ CI/CD Pipeline Flow]
GitHub Actions for Frontend (Vercel)
Vercel automatically deploys on push if you connected via GitHub. No action needed!
But if you want explicit control, create .github/workflows/deploy-frontend.yml:
📄 [GIST 2: deploy-frontend.yml]
GitHub Actions workflow for deploying frontend to Vercel.
Gist URL:hhttps://gist.github.com/urwithajit9/a68c22276c30501a6b4d14a06d86df3b
GitHub Actions for Backend (Railway)
Railway also auto-deploys on push. But for explicit control:
.github/workflows/deploy-backend.yml:
📄 [GIST 3: deploy-backend.yml]
GitHub Actions workflow for deploying backend to Railway.
Gist URL:https://gist.github.com/urwithajit9/cc0026c991cf7669a0083f396b64c9a6
GitHub Actions for Backend (AWS)
For AWS, you need to SSH into the server and pull latest code:
.github/workflows/deploy-aws.yml:
📄 [GIST 4: deploy-aws.yml]
GitHub Actions workflow for deploying to AWS EC2.
Gist URL:https://gist.github.com/urwithajit9/880d644d7026a2cc1cb4bc4d03c32282
Environment-Specific Deployments
Create separate workflows for staging and production:
.github/workflows/
├── deploy-staging.yml # Triggers on push to 'staging' branch
└── deploy-production.yml # Triggers on push to 'main' branch
This lets you test changes in staging before production.
Part I: Monitoring and Maintenance
Production apps need monitoring to catch errors, track performance, and ensure uptime.
Step 1: Error Tracking with Sentry
Sentry captures errors from both frontend and backend.
1. Create Sentry account:
- Go to sentry.io
- Sign up (free tier: 5k errors/month)
- Create a project for Django
- Create a project for Next.js
2. Install Sentry in Django:
pip install sentry-sdk
Add to backend/core/settings/production.py:
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn=os.environ.get('SENTRY_DSN'),
integrations=[DjangoIntegration()],
traces_sample_rate=0.1, # 10% of transactions
send_default_pii=False,
)
Add SENTRY_DSN to environment variables.
3. Install Sentry in Next.js:
npm install @sentry/nextjs
Run configuration wizard:
npx @sentry/wizard@latest -i nextjs
This creates sentry.client.config.js, sentry.server.config.js, etc.
Add NEXT_PUBLIC_SENTRY_DSN to Vercel environment variables.
4. Test error tracking:
Trigger an error in Django:
# In a view
raise Exception("Test error from Django")
Trigger an error in Next.js:
// In a component
throw new Error("Test error from Next.js")
Check Sentry dashboard — errors should appear.
📊 [Exercise: Sentry Error Dashboard]
Instructions: Capture screenshot of Sentry dashboard showing captured errors from both Django and Next.js.
Step 2: Uptime Monitoring
Use UptimeRobot (free tier) to monitor if your site is down.
1. Create account: uptimerobot.com
2. Add monitors:
- Frontend:
https://housing-demo.com(HTTP monitor, check every 5 min) - Backend:
https://api.housing-demo.com/api/properties/cached/(HTTP monitor)
3. Configure alerts:
- Email when down
- Email when back up
- Optional: Slack, Discord, SMS
4. Public status page (optional):
UptimeRobot can generate a public status page: https://stats.uptimerobot.com/ABC123
You can embed this on your site or share with users.
📊 [Exercise: UptimeRobot Dashboard]
Instructions: Capture screenshot showing uptime monitors for frontend and backend with 100% uptime.
Step 3: Performance Monitoring
Option 1: Vercel Analytics (Frontend)
- Vercel → Project → Analytics
- Automatically tracks Web Vitals (LCP, FID, CLS)
- Free on all plans
Option 2: Railway Metrics (Backend)
- Railway → Service → Metrics
- Shows CPU, memory, request latency
- Free on all plans
Option 3: New Relic or Datadog (Advanced)
- Full APM (Application Performance Monitoring)
- Tracks database queries, external API calls, etc.
- Expensive ($100+/month)
For this tutorial, Vercel and Railway built-in metrics are sufficient.
Step 4: Log Aggregation
Railway/Render:
- Built-in logs in dashboard
- Can export to external services
AWS:
- CloudWatch Logs (built-in)
- Or use Papertrail, LogDNA, Datadog
Centralized logging pattern:
All services → Log aggregator (Papertrail) → Search/analyze
This lets you search logs across all services in one place.
Step 5: Database Backups
Neon.tech:
- Automatic daily backups (free tier)
- Point-in-time recovery (paid tiers)
Railway:
- Automatic daily backups
- Can restore from dashboard
RDS:
- Automatic daily backups (configurable)
- Can restore to any point in time (within retention period)
Manual backup:
# Dump database
pg_dump $DATABASE_URL > backup.sql
# Restore
psql $DATABASE_URL < backup.sql
Schedule this with cron or GitHub Actions.
Part J: Cost and Performance Comparison
Let's compare all four deployment paths side by side.
Cost Comparison
| Component | Path 0 (Free) | Path 1 (Railway) | Path 2 (Vercel+Railway) | Path 3 (AWS) |
|---|---|---|---|---|
| Frontend | Free (Vercel) | $5 (Railway) | Free (Vercel Hobby) | $30 (EC2 or Vercel Pro) |
| Backend | Free* (Render) | $5 (Railway) | $5 (Railway) | $30 (EC2) |
| Database | Free** (Neon) | $5 (Railway) | $5 (Railway) | $15 (RDS) |
| Redis | Free*** (Upstash) | $5 (Railway) | $5 (Railway or Upstash) | $15 (ElastiCache) |
| Monitoring | Free (UptimeRobot) | Free (Railway) | Free (Vercel Analytics) | $20 (CloudWatch) |
| SSL | Free (auto) | Free (auto) | Free (auto) | Free (ACM) |
| Domain | Optional $10/year | Optional $10/year | Optional $10/year | Optional $10/year |
| TOTAL/month | $0 | $20 | $15-20 | $110 |
* Sleeps after 15min inactivity
** 0.5GB storage limit
*** 10k requests/day limit
Performance Comparison
Measured from different global locations:
| Location | Path 0 (Free) | Path 1 (Railway) | Path 2 (Vercel+Railway) | Path 3 (AWS) |
|---|---|---|---|---|
| US East | 150ms (cold start) / 80ms (warm) | 50ms | 30ms | 40ms |
| US West | 180ms / 100ms | 80ms | 40ms | 120ms |
| Europe | 250ms / 180ms | 150ms | 60ms | 200ms |
| Asia | 400ms / 300ms | 280ms | 100ms | 350ms |
Winner: Path 2 (Vercel + Railway) — Vercel's global edge network gives best latency worldwide.
📊 [Exercise: Actual Performance Metrics]
Instructions: Use tools like Pingdom or WebPageTest to measure actual TTFB from different locations. Record your results here.
Uptime Comparison
| Deployment Path | Expected Uptime | SLA |
|---|---|---|
| Path 0 (Free) | 95-98% | None |
| Path 1 (Railway) | 99%+ | None (hobby) / 99.9% (pro) |
| Path 2 (Vercel+Railway) | 99.9%+ | 99.9% (Vercel Pro) |
| Path 3 (AWS) | 99.95%+ | 99.99% (with multi-AZ) |
Scalability Comparison
| Deployment Path | Max Concurrent Users | Bottleneck |
|---|---|---|
| Path 0 (Free) | ~50 | Redis rate limit (10k/day) |
| Path 1 (Railway) | ~5,000 | Database connections |
| Path 2 (Vercel+Railway) | ~50,000 | Backend capacity |
| Path 3 (AWS) | ~500,000+ | Cost 💰 |
Decision Matrix
Choose based on:
Stage → Deployment Path
Idea/Demo → Path 0 (Free)
MVP/Beta → Path 1 (Railway)
Launched/Growth → Path 2 (Vercel+Railway)
Enterprise/Scale → Path 3 (AWS)
Part K: Troubleshooting Deployment
Common issues and solutions.
Issue 1: Static Files Not Loading
Symptom: CSS/JS broken, images don't load (404 errors).
Cause: Static files not collected or not served properly.
Fix:
Django:
python manage.py collectstatic --noinput
Make sure in production settings:
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'
With WhiteNoise:
MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
Issue 2: CORS Errors
Symptom: Browser console shows:
Access to fetch at 'https://api.yourdomain.com' has been blocked by CORS policy
Cause: Backend doesn't allow frontend domain in CORS settings.
Fix:
Check Django settings:
CORS_ALLOWED_ORIGINS = [
'https://yourdomain.com',
'https://www.yourdomain.com',
]
Restart backend after changing.
Issue 3: 502 Bad Gateway
Symptom: Nginx shows 502 error.
Cause: Gunicorn not running or crashed.
Fix:
Check Gunicorn status:
sudo systemctl status gunicorn
Check logs:
sudo journalctl -u gunicorn -n 50
Restart:
sudo systemctl restart gunicorn
Issue 4: Database Connection Errors
Symptom:
django.db.utils.OperationalError: could not connect to server
Cause: Wrong DATABASE_URL or database not accessible.
Fix:
Test connection:
psql $DATABASE_URL
Check:
- Is DATABASE_URL correct?
- Is database accepting connections from your server IP?
- For RDS/Railway: Check security groups/firewall rules
- For Neon: Check connection pooler settings
Issue 5: Environment Variables Not Loading
Symptom: Settings reference missing environment variables.
Cause: .env file not loaded or variables not set in platform.
Fix:
Railway/Render/Vercel:
- Check dashboard → Service → Environment Variables
- Redeploy after adding variables
AWS EC2:
- Check
/home/ubuntu/.envfile exists - Source it in systemd service file
Local testing:
export DJANGO_ENV=production
python manage.py check --deploy
Issue 6: SSL Certificate Errors
Symptom:
NET::ERR_CERT_AUTHORITY_INVALID
Cause: SSL not provisioned yet or DNS not propagated.
Fix:
Wait for DNS propagation (up to 48 hours, usually < 2 hours).
Check DNS:
dig yourdomain.com
Force SSL renewal (Certbot):
sudo certbot renew --force-renewal
Issue 7: Next.js Build Fails
Symptom: Deployment fails with TypeScript errors.
Cause: Type errors not caught in development.
Fix:
Test build locally:
cd frontend
npm run build
Fix all TypeScript errors before deploying.
Temporary bypass (not recommended):
// next.config.js
module.exports = {
typescript: {
ignoreBuildErrors: true, // DON'T use in production
},
}
Part L: The Complete Deployment Checklist
Use this checklist for every deployment.
Pre-Deployment Checklist
Code Preparation:
- [ ] All tests passing locally
- [ ] No TypeScript errors (
npm run build) - [ ] No Django warnings (
python manage.py check --deploy) - [ ] Environment variables documented
- [ ]
.gitignoreincludes.env*files - [ ]
requirements.txt/package.jsonup to date
Settings Verification:
- [ ]
DEBUG = Falsein production - [ ]
SECRET_KEYis strong and from environment - [ ]
ALLOWED_HOSTSconfigured - [ ]
CSRF_TRUSTED_ORIGINSconfigured - [ ]
CORS_ALLOWED_ORIGINSconfigured - [ ] Database connection pooling enabled
- [ ] Static files configuration (WhiteNoise or S3)
Security Checklist:
- [ ] SSL redirect enabled (
SECURE_SSL_REDIRECT = True) - [ ] Secure cookies (
SESSION_COOKIE_SECURE = True) - [ ] HSTS enabled
- [ ] No secrets in code (all in environment variables)
- [ ] CORS properly configured (not
CORS_ALLOW_ALL_ORIGINS = True)
Infrastructure Ready:
- [ ] Domain registered (if using custom domain)
- [ ] DNS records planned
- [ ] Hosting accounts created
- [ ] Database provisioned
- [ ] Redis provisioned
Deployment Checklist
Backend Deployment:
- [ ] Code deployed to server/platform
- [ ] Environment variables set
- [ ] Database migrations run
- [ ] Static files collected
- [ ] Gunicorn/service started
- [ ] Health check endpoint responding
Frontend Deployment:
- [ ] Code deployed to Vercel/platform
- [ ] Environment variables set (
NEXT_PUBLIC_API_URL) - [ ] Build successful
- [ ] Preview URL accessible
Database Setup:
- [ ] Migrations applied
- [ ] Superuser created
- [ ] Test data loaded (if needed)
- [ ] Backups configured
Domain and SSL:
- [ ] DNS records added
- [ ] DNS propagated (check with
dig) - [ ] SSL certificates provisioned
- [ ] HTTPS enforced
- [ ] HTTP redirects to HTTPS
Integration Testing:
- [ ] Frontend loads without errors
- [ ] API calls work (check DevTools Network tab)
- [ ] No CORS errors
- [ ] Images load from Cloudinary
- [ ] Authentication works (if implemented)
- [ ] Database queries succeed
Post-Deployment Checklist
Monitoring Setup:
- [ ] Error tracking configured (Sentry)
- [ ] Uptime monitoring configured (UptimeRobot)
- [ ] Performance monitoring enabled
- [ ] Log aggregation set up
- [ ] Alerts configured (email/Slack)
Backups Verified:
- [ ] Database backups enabled
- [ ] Backup restoration tested
- [ ] Backup schedule confirmed
Performance Verification:
- [ ] Lighthouse score > 90
- [ ] TTFB < 200ms
- [ ] No console errors
- [ ] API response times < 500ms
- [ ] Images loading fast
Documentation:
- [ ] Deployment process documented
- [ ] Environment variables documented
- [ ] Rollback procedure documented
- [ ] Incident response plan created
Load Testing:
- [ ] Test with expected traffic volume
- [ ] Verify database connection pooling works
- [ ] Verify Redis cache hit rate
- [ ] No memory leaks under sustained load
User Acceptance:
- [ ] Staging environment tested
- [ ] Stakeholders approve
- [ ] Users can access the site
- [ ] All features work in production
What We Built — The Complete System
We entered Part 7 with a housing portal running on localhost. We exit with four complete deployment paths to choose from.
Path 0 (Free Tier):
- Perfect for this tutorial
- $0/month, ideal for demos and MVPs
- Accept cold starts as a tradeoff
- Easy migration path to paid tiers when needed
Path 1 (Railway):
- Simplest paid deployment
- $20/month for everything in one place
- No infrastructure management
- Great for side projects and small apps
Path 2 (Vercel + Railway):
- Optimal for production
- $15-35/month
- Best performance globally
- This is what you choose for a real product
Path 3 (AWS DIY):
- Full control
- $90+/month
- For enterprise and compliance needs
- Requires DevOps knowledge
📊 [Complete Production Request Flow]
The Production Architecture (Path 2)
User in Tokyo
↓
Vercel Edge (Tokyo) — serves Next.js SSR, cached static files
↓
Railway Backend (US-East) — Django API
↓
├─ PostgreSQL (Railway) — persistent data
├─ Redis (Railway) — cache layer
└─ Cloudinary CDN (Tokyo edge) — images
Result: < 100ms TTFB globally
Every layer is optimized. Every cache works together. The user experience is instant.
What's Different from Development
Development (localhost):
- DEBUG = True (detailed errors)
- SQLite or PostgreSQL on same machine
- Redis on same machine
- No SSL
- No monitoring
- Single server
- Unlimited resources
Production (live site):
- DEBUG = False (generic errors)
- Managed PostgreSQL with backups
- Managed Redis or ElastiCache
- SSL enforced
- Error tracking, uptime monitoring
- Distributed across multiple servers/regions
- Resource limits, cost constraints
- Autoscaling
- Security hardening
Beyond Deployment — What's Next
The series ends here, but your learning continues:
Part 8 (Bonus): Advanced Topics
- Celery background tasks
- WebSockets for real-time updates
- Full-text search with Elasticsearch
- Payment integration (Stripe)
- Email notifications (SendGrid)
- Admin customization
Part 9 (Bonus): Scaling Beyond
- Kubernetes deployment
- Microservices architecture
- Multi-region deployment
- Database sharding
- Read replicas
But you don't need any of that yet. What you have now — this complete, optimized, deployed housing portal — is production-ready and can handle thousands of users.
Checkpoint: Push to GitHub
git add .
git commit -m "feat: add production settings, deployment configs, CI/CD pipeline"
git checkout -b part-7-deployment
git push origin part-7-deployment
The repo now has seven branches:
-
part-1-setup— infrastructure -
part-2-data— database and seed -
part-3-caching— API and Redis -
part-4-optimization— query optimization and signals -
part-5-frontend— Next.js UI and SWR -
part-6-images— Cloudinary and lazy loading -
part-7-deployment— production deployment
View the live site:
- Free tier:
https://your-app.vercel.app - With domain:
https://housing-demo.com
Acknowledgments and References
This deployment guide was informed by:
- Django Deployment Guide by SaaS Pegasus — Comprehensive Django deployment resource
- Vercel Documentation — Next.js deployment best practices
- Railway Documentation — PaaS deployment patterns
- AWS Well-Architected Framework — Cloud architecture principles
- Django Official Deployment Checklist — Security and settings verification
Special thanks to the Django, Next.js, and Railway communities for excellent documentation.
The series is complete. You've built a production-grade housing portal from zero to deployed. You understand caching at every layer. You know how to optimize databases. You can deploy to production. Most importantly, you understand the why behind every decision.
Now go build something amazing. 🚀
Series Complete:
- Part 1: Infrastructure
- Part 2: Database
- Part 3: Caching
- Part 4: Optimization
- Part 5: Frontend
- Part 6: Images
- Part 7: Deployment ← You are here



Top comments (0)