Conduit Deployment
Production-ready containerized deployment for the Conduit application (Medium.com clone).
Table of Contents
Description
This repository contains the deployment configuration for the Conduit full-stack application.
Components:
- Backend: Django REST API with Gunicorn WSGI server
- Frontend: Angular Single Page Application (SPA) served via Nginx
- Database: PostgreSQL for data persistence
- Proxy: Nginx reverse proxy for static files and API routing
Purpose: This is a deployment-only repository. The application source code (frontend and backend) are maintained in separate repositories and cloned into this structure.
Prerequisites
- Docker: Version 20.10+
- Docker Compose: Version 2.0+
- Git: For cloning repositories
Verify:
docker --version
docker compose version
Quickstart
1. Clone this deployment repo
git clone -b feature/container-deployment git@github.com:Ozinho78/conduit-deployment.git
cd conduit-deployment
2. Clone application repositories
git clone git@github.com:Ozinho78/conduit-backend.git
git clone git@github.com:Ozinho78/conduit-frontend.git
3. Copy files
The script copies all necessary files into frontend and backend directory
# Windows
.\deploy.ps1
# Linux
chmod +x ./deploy.sh
./deploy.sh
Configure backend for PostgreSQL
5. Create .env file
Set POSTGRES_PASSWORD, DJANGO_SECRET_KEY, YOUR_VM_IP
cp .env.example .env
nano .env
6. Start services
docker compose up -d --build
7. Access application
Frontend: http://<YOUR_VM_IP>:8282
Backend-Admin: http://<YOUR_VM_IP>:8000/admin
Backend-API: http://<YOUR_VM_IP>:8000/api
Usage
Initial Setup
Directory structure after setup:
conduit-deployment/
├── backend.Dockerfile # Multi-stage build: Django app + Gunicorn WSGI + Nginx reverse proxy
├── frontend.Dockerfile # Multi-stage build: Angular build → Nginx static file serving
├── nginx-backend.conf # Nginx config: static files + proxy_pass to Gunicorn
├── nginx-frontend.conf # Nginx config: serves Angular dist/ on port 8282
├── docker-compose.yaml # Orchestration: frontend, backend, postgres services
├── .env.example # Template for .env, contains environment variables
├── .env # Runtime environment variables (NOT in Git!), created by YOU
├── .gitignore # Excludes .env, logs, IDE configs, OS files
├── README.md # This document, project documentation with ToC, quickstart, usage
├── backend.dockerignore # Excludes Python cache, venv, .git from Docker context
├── frontend.dockerignore # Excludes node_modules, .git, dist from Docker context
├── conduit-backend/ # Cloned from separate repo
│ └── conduit/
│ └── settings.py # Must be modified for production (ALLOWED_HOSTS, DB config)
└── conduit-frontend/ # Cloned from separate repo
Backend PostgreSQL Configuration
CRITICAL: The backend must be modified to use PostgreSQL.
Step 1: Add PostgreSQL dependencies
Edit conduit-backend/requirements.txt and add:
psycopg2-binary==2.7.7
django-cors-headers==2.1.0
gunicorn==19.9.0
Step 2: Update Django settings
Edit conduit-backend/conduit/settings.py:
Replace the DATABASES configuration:
import os
# PostgreSQL Database (replaces SQLite!)
DATABASES = {
'default': {
'ENGINE': os.environ.get('DATABASE_ENGINE', 'django.db.backends.postgresql'),
'NAME': os.environ.get('DATABASE_NAME', 'conduit'),
'USER': os.environ.get('DATABASE_USER', 'conduit'),
'PASSWORD': os.environ.get('DATABASE_PASSWORD', ''),
'HOST': os.environ.get('DATABASE_HOST', 'database'),
'PORT': os.environ.get('DATABASE_PORT', '5432'),
'CONN_MAX_AGE': 600,
}
}
Update ALLOWED_HOSTS:
# ALLOWED_HOSTS from environment
ALLOWED_HOSTS_ENV = os.environ.get('DJANGO_ALLOWED_HOSTS', 'localhost,127.0.0.1')
ALLOWED_HOSTS = [host.strip() for host in ALLOWED_HOSTS_ENV.split(',')]
# Add Docker service names (important for Nginx!)
ALLOWED_HOSTS.extend(['backend', 'conduit-backend'])
Update SECRET_KEY:
# Secret Key from environment (required!)
SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
if not SECRET_KEY:
raise ValueError("DJANGO_SECRET_KEY environment variable must be set!")
Update DEBUG:
# Debug mode
DEBUG = os.environ.get('DJANGO_DEBUG', 'False').lower() in ('true', '1', 'yes')
Add CORS (if not already present):
Add to INSTALLED_APPS:
INSTALLED_APPS = [
# ...
'corsheaders',
]
Add to MIDDLEWARE (at the top!):
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
# ... rest
]
Add CORS configuration:
# CORS
CORS_ALLOWED_ORIGINS_ENV = os.environ.get('CORS_ALLOWED_ORIGINS', 'http://localhost:8282')
CORS_ALLOWED_ORIGINS = [origin.strip() for origin in CORS_ALLOWED_ORIGINS_ENV.split(',')]
CORS_ALLOW_CREDENTIALS = True
Add Static/Media files configuration:
# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# Media files (User uploads)
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'mediafiles')
Environment Configuration
Create .env file:
cp .env.example .env
nano .env
Required settings:
# PostgreSQL (MUST be set!)
POSTGRES_PASSWORD=your_secure_password_here
# Django Secret Key (generate it!)
DJANGO_SECRET_KEY=your_generated_secret_key_here
# Your VM IP (replace YOUR_VM_IP_HERE!)
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,backend,conduit-backend,<YOUR_VM_IP_HERE>
CORS_ALLOWED_ORIGINS=http://localhost:8282,http://<YOUR_VM_IP_HERE>:8282
Generate Django Secret Key:
python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'
Building and Running
Build and start all services:
docker compose up -d --build
Access application
Frontend: http://<YOUR_VM_IP>:8282
Backend-Admin: http://<YOUR_VM_IP>:8000/admin
Backend-API: http://<YOUR_VM_IP>:8000/api
View logs:
# All services
docker compose logs -f
# Specific service
docker compose logs -f backend
docker compose logs -f frontend
Check status:
docker compose ps
Stop services:
Stop (keeps data)
docker compose stop
Stop and remove containers (keeps volumes)
docker compose down
Stop and remove everything including data
[!WARNING]
Deletes database!
docker compose down -v
Logs
View logs:
# All services
docker compose logs
# Follow live
docker compose logs -f
# Specific service
docker compose logs backend
docker compose logs frontend
docker compose logs database
Save logs to file:
# Backend logs
docker logs conduit-backend > backend-logs.txt
# Frontend logs
docker logs conduit-frontend > frontend-logs.txt
# Database logs
docker logs conduit-postgres > database-logs.txt
# All logs with timestamps
docker compose logs --timestamps > all-logs.txt
Database
PostgreSQL configuration (.env):
POSTGRES_DB=conduit
POSTGRES_USER=conduit
POSTGRES_PASSWORD=your_password
Last Updated: December 2025 Course: DevSecOps Project: Conduit Containerization