Continuous Delivery for Django on DigitalOcean with GitHub Actions & Docker
TL;DR: In this guide, you’ll learn how to build a production-grade Continuous Delivery (CD) pipeline for your Django app using GitHub Actions, Docker, and a DigitalOcean Ubuntu VM. We’ll automate everything from testing to deployment—so your app updates with every push to main.
💡 What is Continuous Delivery?
Continuous Delivery is a software practice where code is always in a deployable state. It allows teams to:
- ✅ Ship faster
- 🔄 Automate deployments
- 🔐 Reduce human error
- 🚀 Release confidently
We’ll implement a pipeline that looks like this:
🔄 CD Pipeline Workflow
graph TD
A[Push code to GitHub] --> B[GitHub Actions CI/CD triggered]
B --> C[Checkout code]
C --> D[SSH to DigitalOcean VM]
D --> E[Pull latest code from Git]
E --> F[Rebuild Docker containers]
F --> G[Restart Django app via Docker Compose]
G --> H[Live site is updated automatically]
🔍 Step-by-Step Summary
| Step | Description |
|---|---|
| A | Developer pushes code to main |
| B | GitHub Actions starts the CI/CD workflow |
| C | Code is checked out in the GitHub runner |
| D | GitHub connects to your DigitalOcean VM via SSH |
| E | Latest code is pulled into your server |
| F | Docker containers are rebuilt |
| G | Docker Compose restarts the app |
| H | Nginx serves the latest version to users |
🛠️ Tools We’ll Use
| Role | Tool |
|---|---|
| CI/CD | GitHub Actions |
| Deployment | DigitalOcean (Ubuntu VM) |
| Containerization | Docker + Docker Compose |
| Web Server | Nginx |
| Secure Access | SSH with GitHub Secrets |
📦 Step 1: Dockerize Your Django App
Dockerfile
FROM python:3.11-slim
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]
🐳 Step 2: Docker Compose for Production
docker-compose.prod.yml
version: '3.9'
services:
web:
build: .
env_file: .env
volumes:
- .:/app
expose:
- 8000
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- web
🌐 Step 3: Nginx Configuration
nginx.conf
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
🔐 Step 4: SSH Setup for GitHub
- On your local machine:
ssh-keygen -t ed25519 -C "your_email@example.com"
- Copy the public key to your server’s
~/.ssh/authorized_keys - Add the private key to your GitHub repo secrets:
→DO_SSH_PRIVATE_KEY
🤖 Step 5: GitHub Actions Workflow
.github/workflows/deploy.yml
name: Django CD to DigitalOcean
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.DO_SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan your.server.ip >> ~/.ssh/known_hosts
- name: Deploy via SSH
run: |
ssh -i ~/.ssh/id_ed25519 deploy@your.server.ip << 'EOF'
cd /home/deploy/myproject/
git pull origin main
docker compose -f docker-compose.prod.yml down
docker compose -f docker-compose.prod.yml up -d --build
EOF
Replace:
your.server.ipwith your actual VM IPdeploywith your username
🔧 Step 6: Production .env Example
DJANGO_SECRET_KEY=supersecret
DEBUG=False
ALLOWED_HOSTS=yourdomain.com
📁 Folder Structure on the Server
/home/deploy/myproject/
├── manage.py
├── myproject/
├── Dockerfile
├── docker-compose.prod.yml
├── .env
├── nginx.conf
✅ Final Result
Every time you push to main, your app is:
- Pulled to your server
- Rebuilt with Docker
- Restarted with zero manual steps
🌟 Bonus Ideas
- Add Sentry for error monitoring
- Auto-restart using systemd
- Setup Let’s Encrypt for HTTPS
🧠 Conclusion
You’ve now built a real Continuous Delivery pipeline for Django on a live Ubuntu VM. It’s reliable, repeatable, and scales with your app.
Get in Touch with us
Related Posts
- The Accounting Software Your Firm Uses Is Built for Your Clients, Not for You
- 2026年本地大模型(Local LLM)硬件选型实用指南
- Choosing Hardware for Local LLMs in 2026: A Practical Sizing Guide
- Why Your Finance Team Spends 40% of Their Week on Work AI Can Now Do
- 用纯开源方案搭建生产级 SOC:Wazuh + DFIR-IRIS + 自研集成层实战记录
- How We Built a Real Security Operations Center With Open-Source Tools
- FarmScript:我们如何从零设计一门农业IoT领域特定语言
- FarmScript: How We Designed a Programming Language for Chanthaburi Durian Farmers
- 智慧农业项目为何止步于试点阶段
- Why Smart Farming Projects Fail Before They Leave the Pilot Stage
- ERP项目为何总是超支、延期,最终令人失望
- ERP Projects: Why They Cost More, Take Longer, and Disappoint More Than Expected
- AI Security in Production: What Enterprise Teams Must Know in 2026
- 弹性无人机蜂群设计:具备安全通信的无领导者容错网状网络
- Designing Resilient Drone Swarms: Leaderless-Tolerant Mesh Networks with Secure Communications
- NumPy广播规则详解:为什么`(3,)`和`(3,1)`行为不同——以及它何时会悄悄给出错误答案
- NumPy Broadcasting Rules: Why `(3,)` and `(3,1)` Behave Differently — and When It Silently Gives Wrong Answers
- 关键基础设施遭受攻击:从乌克兰电网战争看工业IT/OT安全
- Critical Infrastructure Under Fire: What IT/OT Security Teams Can Learn from Ukraine’s Energy Grid
- LM Studio代码开发的系统提示词工程:`temperature`、`context_length`与`stop`词详解













