On Ubuntu/Rocky Linux:
sudo apt update && sudo apt install -y docker.io docker-compose # Ubuntu
sudo yum install -y docker docker-compose # Rocky Linux
Start and enable Docker:
sudo systemctl start docker
sudo systemctl enable docker
Verify installation:
docker --version
docker-compose --version
Inside the project root:
touch Dockerfile
touch docker-compose.yml
mkdir backend
Dockerfile
Create Dockerfile
in the project root:
# Use official Python image
FROM python:3.11
# Set working directory
WORKDIR /app
# Copy requirements file and install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy project files
COPY . .
# Set environment variables
ENV PYTHONUNBUFFERED=1
# Expose port
EXPOSE 8000
# Start the application
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "backend.wsgi:application"]
docker-compose.yml
Create docker-compose.yml
in the project root:
version: '3.8'
services:
web:
build: .
container_name: django_app
ports:
- "8000:8000"
depends_on:
- db
volumes:
- .:/app
environment:
- DJANGO_SECRET_KEY=supersecretkey
- DJANGO_DEBUG=True
- DATABASE_URL=postgres://user:password@db:5432/dbname
db:
image: postgres:15
container_name: postgres_db
restart: always
environment:
POSTGRES_DB: dbname
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- pg_data:/var/lib/postgresql/data
volumes:
pg_data:
Modify settings.py
:
import os
import dj_database_url
SECRET_KEY = os.getenv("DJANGO_SECRET_KEY", "fallback-secret")
DEBUG = os.getenv("DJANGO_DEBUG", "False") == "True"
DATABASES = {
"default": dj_database_url.config(default="sqlite:///db.sqlite3")
}
Build and start the containers:
docker-compose up --build -d
Check running containers:
docker ps
View logs:
docker-compose logs -f web
Stop containers:
docker-compose down
Restart:
docker-compose up -d
Run migrations inside the container:
docker-compose exec web python manage.py migrate
Create a superuser:
docker-compose exec web python manage.py createsuperuser
Modify docker-compose.yml
for hot-reloading:
command: >
sh -c "python manage.py runserver 0.0.0.0:8000"
Rebuild and restart:
docker-compose up --build -d
Modify docker-compose.yml
:
version: '3.8'
services:
web:
build: .
container_name: django_app
expose:
- "8000"
depends_on:
- db
volumes:
- .:/app
environment:
- DJANGO_SECRET_KEY=supersecretkey
- DJANGO_DEBUG=False
- DATABASE_URL=postgres://user:password@db:5432/dbname
db:
image: postgres:15
container_name: postgres_db
restart: always
environment:
POSTGRES_DB: dbname
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- pg_data:/var/lib/postgresql/data
nginx:
image: nginx:latest
container_name: nginx_proxy
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- web
volumes:
pg_data:
Create nginx.conf
:
server {
listen 80;
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Rebuild and restart:
docker-compose up --build -d
Modify Dockerfile
:
CMD ["gunicorn", "--workers=3", "--bind=0.0.0.0:8000", "backend.wsgi:application"]
Rebuild and restart:
docker-compose up --build -d
docker-compose logs -f web
docker-compose logs -f db
docker-compose logs -f nginx
Inspect running containers:
docker stats
Create .github/workflows/deploy.yml
:
name: Deploy Django with Docker
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: SSH into Server and Deploy
uses: appleboy/ssh-action@master
with:
host: $
username: $
key: $
script: |
cd /home/ubuntu/django-docker
git pull origin main
docker-compose down
docker-compose up --build -d
Add secrets in GitHub repository:
SERVER_IP
SERVER_USER
SSH_PRIVATE_KEY