A complete Dockerized Django Notes Application demonstrating containerization, multi-container orchestration, networking, persistent storage, healthchecks, environment configuration, and Nginx reverse proxy.
This screenshot shows the final output of the Django Notes application.
This screenshot shows the project source code and structure in VS Code.
This screenshot shows the Docker image being built successfully.
πΊ Train with Shubham β Dockerizing Django Application
π https://youtu.be/9bSbNNH4Nqw
- Project Overview
- Architecture
- Technology Stack
- Project Structure
- How the Application Works
- Docker Components
- Environment Variables
- Docker Compose
- Database
- Nginx Reverse Proxy
- Docker Networking
- Docker Volumes
- Healthchecks
- Setup & Installation
- Run the Project
- Verify the Application
- Useful Docker Commands
- Database Commands
- Logs & Troubleshooting
- Common Problems
- Security Best Practices
- DevOps Concepts Demonstrated
- Learning Outcomes
- Future Improvements
- Author
This project demonstrates how a traditional web application can be converted into a multi-container Docker application.
The application uses:
- Django for backend/application logic
- MySQL for persistent application data
- Nginx as a reverse proxy
- Docker for containerization
- Docker Compose for orchestration
.envfor configuration and database credentials- Docker volumes for database persistence
- Docker healthchecks to verify service readiness
The project follows a simple three-tier architecture:
USER / BROWSER
|
| HTTP :80
v
+--------------+
| NGINX |
| Reverse Proxy|
+--------------+
|
| HTTP :8000
v
+--------------+
| DJANGO |
| Backend |
+--------------+
|
| MySQL :3306
v
+--------------+
| MYSQL |
| Database |
+--------------+
|
v
DB VOLUME
The application is divided into three main services:
Django is the main application/backend service.
Responsibilities:
- Runs the Python/Django application
- Handles application requests
- Connects to MySQL
- Runs database migrations
- Serves the application through Gunicorn
Typical internal port:
8000
MySQL stores the application's persistent data.
Responsibilities:
- Store application records
- Provide database services to Django
- Persist data through Docker volumes
Typical internal port:
3306
Example configuration:
DB_NAME=test_db
DB_USER=root
DB_PASSWORD=root
DB_HOST=db_cont
DB_PORT=3306
db_contis the Docker container hostname used by Django to reach MySQL. It is not a MySQL account name.
Nginx acts as the reverse proxy and public entry point.
Responsibilities:
- Accept browser requests
- Listen on port
80 - Forward requests to Django
- Provide a single public endpoint for the application
Typical flow:
Browser
β
localhost:80
β
Nginx
β
Django:8000
β
MySQL:3306
| Technology | Purpose |
|---|---|
| Docker | Containerization |
| Docker Compose | Multi-container orchestration |
| Django | Backend/application framework |
| Python | Backend programming language |
| MySQL | Database |
| Nginx | Reverse proxy/web server |
| Gunicorn | Django application server |
| Docker Network | Container-to-container communication |
| Docker Volume | Persistent database storage |
.env |
Environment configuration |
| Docker Scout | Image vulnerability scanning |
A typical project structure is:
docker-devops-project/
β
βββ app/
β βββ backend/
β β βββ manage.py
β β βββ requirements.txt
β β βββ <django-project>/
β β βββ <django-app>/
β β
β βββ frontend/
β βββ public/
β βββ src/
β
βββ nginx/
β βββ nginx.conf
β
βββ Dockerfile
βββ docker-compose.yml
βββ .env
βββ .dockerignore
βββ README.md
The exact folder names can vary depending on the repository implementation. Keep this structure aligned with your actual project files.
When a user opens:
http://localhost
the request follows this path:
1. Browser
β
2. Nginx :80
β
3. Django/Gunicorn :8000
β
4. Django processes request
β
5. Django communicates with MySQL :3306
β
6. MySQL returns data
β
7. Django returns response
β
8. Nginx sends response to browser
This separation makes the application easier to deploy, maintain, scale, and troubleshoot.
The Dockerfile defines how the application image is built.
Typical responsibilities:
- Select a base image
- Set the working directory
- Install dependencies
- Copy application source code
- Configure the application
- Define the startup command
Example conceptual flow:
Base Python Image
β
Install Dependencies
β
Copy Application
β
Configure Environment
β
Run Django/Gunicorn
The .env file stores configuration values outside the application source code.
Example:
DB_NAME=test_db
DB_USER=root
DB_PASSWORD=root
DB_PORT=3306
DB_HOST=db_cont| Variable | Meaning |
|---|---|
DB_NAME |
MySQL database name |
DB_USER |
Database username |
DB_PASSWORD |
Database password |
DB_PORT |
MySQL port |
DB_HOST |
Docker hostname of MySQL |
For real production deployments, do not commit passwords or secrets to GitHub.
Use:
.env
and add it to .gitignore:
.envdocker-compose.yml is the central blueprint of the application.
It defines:
- Services
- Images/builds
- Ports
- Environment variables
- Networks
- Volumes
- Dependencies
- Healthchecks
- Restart behavior
Example architecture:
services:
db:
image: mysql
container_name: db_cont
django:
build: .
container_name: django_cont
nginx:
image: nginx
container_name: nginx_contThe project uses MySQL.
The database is created/configured through the MySQL container environment.
Example:
MYSQL_DATABASE=test_db
MYSQL_ROOT_PASSWORD=rootDjango connects using:
DB_NAME=test_db
DB_USER=root
DB_PASSWORD=root
DB_HOST=db_cont
DB_PORT=3306No.
If MySQL is running inside Docker, you do not need to separately install MySQL on your Windows machine.
You also do not normally need to manually create Django tables.
Django migrations handle application tables:
python manage.py migrateDocker Compose creates an internal network for the services.
Containers can communicate using service/container names rather than manually configured IP addresses.
For example:
DB_HOST=db_contDjango can reach MySQL through:
db_cont:3306
Conceptually:
django_cont
|
| Docker Network
|
+------> db_cont:3306
This is more reliable than hardcoding a container IP address.
Database containers should use persistent storage.
Without a volume:
Container deleted
β
Database data may be lost
With a volume:
Container deleted
β
Volume remains
β
Database data remains
Example:
volumes:
mysql_data:and:
services:
db:
volumes:
- mysql_data:/var/lib/mysqlThis is especially important for databases.
One of the important problems in this project is database startup timing.
MySQL may take several seconds to initialize.
If Django starts immediately:
Django starts
β
Connect to MySQL
β
MySQL not ready
β
Connection error
β
Django exits/restarts
A healthcheck improves this:
MySQL starts
β
Healthcheck
β
MySQL ready
β
Database becomes healthy
β
Django starts
β
Django connects successfully
Example:
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 5s
retries: 10Then Compose can use dependency conditions so Django waits for a healthy database.
Nginx provides the public entry point.
Instead of exposing Django directly to users:
User β Django
the architecture uses:
User
β
Nginx
β
Django
- Reverse proxy
- Centralized traffic handling
- Static file serving
- SSL/TLS termination capability
- Access control
- Caching capability
- Better production architecture
- Easier future scaling
Install:
- Docker Desktop
- Git
- A code editor such as VS Code
Verify Docker:
docker --versionVerify Compose:
docker compose versionVerify Git:
git --versiongit clone https://github.com/hritikranjan1/django-notes-app.gitMove into the project:
cd django-notes-appCreate:
.env
Example:
DB_NAME=test_db
DB_USER=root
DB_PASSWORD=root
DB_PORT=3306
DB_HOST=db_contYour Compose MySQL configuration must use compatible database credentials.
For example:
MYSQL_DATABASE=test_db
MYSQL_ROOT_PASSWORD=rootDo not expose real production credentials in README files or public repositories.
Recommended modern Docker Compose command:
docker compose up -d --buildThis will:
- Build the application image
- Pull required images
- Create networks
- Create volumes
- Create containers
- Start MySQL
- Wait for required health conditions
- Start Django
- Start Nginx
Run:
docker psExpected architecture:
db_cont
django_cont
nginx_cont
Example:
CONTAINER ID IMAGE STATUS
xxxxxx mysql Up (healthy)
xxxxxx django_app Up (healthy)
xxxxxx nginx Up
Open:
http://localhost
This is the preferred application entry point.
If port 8000 is exposed:
http://localhost:8000
This bypasses Nginx and is useful for troubleshooting.
PowerShell:
curl.exe http://localhost:8000A successful response should contain:
HTTP/1.1 200 OK
or an equivalent successful HTTP response.
curl.exe http://localhostThen open:
http://localhost
in a browser.
If the MySQL container is named:
db_cont
run:
docker exec -it db_cont mysql -uroot -prootThen:
SHOW DATABASES;Select the database:
USE test_db;Check tables:
SHOW TABLES;Exit:
exit;Enter the Django container:
docker exec -it django_cont shThen:
python manage.py migrateCheck migrations:
python manage.py showmigrationsExit:
exitdocker compose logsFollow logs:
docker compose logs -fdocker logs django_contFollow:
docker logs -f django_contdocker logs db_contdocker logs nginx_contdocker compose psor:
docker psHealthy example:
db_cont Up (healthy)
django_cont Up (healthy)
nginx_cont Up
docker compose downThis stops and removes containers and the Compose network.
Named volumes are normally preserved unless explicitly removed.
After stopping:
docker compose up -dIf code or Docker configuration changed:
docker compose up -d --builddocker compose downTo also remove volumes:
docker compose down -vUse down -v carefully.
If you need to rebuild images:
docker compose down
docker compose build --no-cache
docker compose up -dDo not use down -v unless you intentionally want to remove database volumes.
docker psAll containers:
docker ps -adocker imagesdocker volume lsdocker network lsdocker inspect django_contdocker exec -it django_cont shdocker compose restart djangodocker compose stop djangodocker compose start djangoError:
django.db.utils.OperationalError:
Can't connect to server on 'db_cont'
Possible reason:
MySQL is still initializing.
Check:
docker compose psThen:
docker logs db_contLook for:
ready for connections
Healthchecks and depends_on should be configured so Django waits for database readiness.
Check:
docker logs django_contCommon causes:
- Database unavailable
- Incorrect
.env - Missing Python package
- Migration failure
- Incorrect Django settings
- Incorrect startup command
Check:
docker logs nginx_contThen:
docker logs django_contTest Django directly:
curl.exe http://localhost:8000If port 8000 works but port 80 does not, investigate the Nginx configuration.
If Docker reports:
port is already allocated
find the process using the port.
On Windows:
netstat -ano | findstr :80For port 8000:
netstat -ano | findstr :8000Run:
docker exec -it django_cont python manage.py migrateThen:
docker exec -it db_cont mysql -uroot -prootand:
USE test_db;
SHOW TABLES;After changing environment configuration, recreate/restart the services:
docker compose down
docker compose up -d --buildIf required, force recreation:
docker compose up -d --force-recreateFor learning, values such as:
DB_USER=root
DB_PASSWORD=rootare acceptable.
For production:
- Do not use the MySQL
rootaccount for the application. - Use a dedicated database user.
- Use a strong password.
- Do not commit
.envto Git. - Use secrets management.
- Scan images for vulnerabilities.
- Keep base images updated.
- Use least-privilege permissions.
- Expose only required ports.
- Configure HTTPS.
- Restrict database access to the internal Docker network.
Docker Scout can be used to identify vulnerabilities in container images.
Example:
docker scout quickviewYou can also inspect an image:
docker scout cves <image-name>The goal is to identify vulnerable packages and update the relevant base images/dependencies.
For applications that require a build stage, multi-stage Dockerfiles separate build dependencies from the final runtime image.
Concept:
BUILD STAGE
-----------
Install build tools
Install dependencies
Build application
|
v
RUNTIME STAGE
-------------
Copy only required output
Run application
Benefits:
- Smaller images
- Fewer unnecessary packages
- Reduced attack surface
- Faster deployments
.dockerignore prevents unnecessary files from being sent to Docker during image builds.
Typical entries:
.git
.gitignore
.env
__pycache__
*.pyc
venv
node_modules
README.md
This can improve build performance and prevent sensitive/unnecessary files from entering the build context.
This project is useful as a DevOps learning project because it demonstrates:
Application β Docker Image β Container
Docker Compose
β
Django + MySQL + Nginx
django_cont β db_cont:3306
Client β Nginx β Django
MySQL Container β Docker Volume
MySQL β Healthcheck β Django
.env β Docker Compose β Container
Docker Image β Docker Scout β Vulnerability Analysis
After completing this project, you should be able to explain:
- What Docker is
- Why containers are useful
- Difference between image and container
- What Docker Compose does
- How multiple containers communicate
- How Docker networking works
- Why service names can be used as hostnames
- Why databases require persistent volumes
- Why healthchecks are important
- What
depends_ondoes - Why Nginx is used as a reverse proxy
- How Django connects to MySQL
- How environment variables are passed to containers
- How to inspect container logs
- How to troubleshoot startup failures
- How to expose container ports
- How to rebuild and restart services
- How to perform Django migrations inside a container
- How container image vulnerabilities can be scanned
If an interviewer asks:
You can answer:
"I created a multi-container Django Notes Application using Docker and Docker Compose. The application uses Django as the backend, MySQL for persistent data storage, and Nginx as a reverse proxy. Docker Compose manages the complete application stack, including networking, volumes, environment variables, service dependencies, and healthchecks. Django connects to MySQL through the Docker network using the database container name as the hostname. I also implemented database persistence using Docker volumes and used healthchecks to prevent Django from starting before MySQL was ready. Nginx exposes the application through port 80 and forwards requests to the Django service."
ββββββββββββββββββββββββ
β Browser β
ββββββββββββ¬ββββββββββββ
β
HTTP :80
β
βΌ
ββββββββββββββββββββββββ
β Nginx β
β Reverse Proxy β
ββββββββββββ¬ββββββββββββ
β
:8000
β
βΌ
ββββββββββββββββββββββββ
β Django β
β Gunicorn β
ββββββββββββ¬ββββββββββββ
β
:3306
β
βΌ
ββββββββββββββββββββββββ
β MySQL β
β test_db β
ββββββββββββ¬ββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Docker Volume β
β Persistent DB Data β
ββββββββββββββββββββββββ
# Build and start
docker compose up -d --build
# Check containers
docker compose ps
# View logs
docker compose logs -f
# Django logs
docker logs -f django_cont
# MySQL logs
docker logs -f db_cont
# Nginx logs
docker logs -f nginx_cont
# Enter Django container
docker exec -it django_cont sh
# Run migrations
docker exec -it django_cont python manage.py migrate
# Connect to MySQL
docker exec -it db_cont mysql -uroot -proot
# Stop
docker compose down
# Rebuild
docker compose up -d --build
# Remove volumes - USE CAREFULLY
docker compose down -vWhen running locally:
Application through Nginx:
http://localhost
Django direct:
http://localhost:8000
MySQL:
localhost:3306
The recommended browser URL is:
http://localhost
because it represents the intended reverse-proxy architecture.
Before considering the deployment successful, verify:
[ ] Docker installed
[ ] Docker Compose available
[ ] Repository cloned
[ ] .env configured
[ ] Dockerfile available
[ ] docker-compose.yml available
[ ] Nginx configuration available
[ ] Images built successfully
[ ] MySQL container running
[ ] MySQL container healthy
[ ] Django container running
[ ] Django container healthy
[ ] Nginx container running
[ ] Database migrations completed
[ ] Database volume configured
[ ] http://localhost works
[ ] Django logs show no critical errors
When the application does not work, troubleshoot in this order:
1. Check Docker
β
docker --version
2. Check containers
β
docker compose ps
3. Check MySQL
β
docker logs db_cont
4. Check Django
β
docker logs django_cont
5. Check Nginx
β
docker logs nginx_cont
6. Test Django directly
β
curl.exe http://localhost:8000
7. Test Nginx
β
curl.exe http://localhost
8. Check database
β
docker exec -it db_cont mysql -uroot -proot
This approach helps identify whether the problem is with the database, backend, reverse proxy, networking, or application configuration.
Possible next steps for this project:
- Add HTTPS with SSL/TLS
- Use a non-root MySQL application user
- Add Redis
- Add Celery for background jobs
- Add CI/CD with GitHub Actions or Jenkins
- Push Docker images to Docker Hub/Amazon ECR
- Deploy to AWS EC2/ECS
- Add Prometheus and Grafana monitoring
- Add centralized logging
- Add automated tests
- Add image vulnerability scanning to CI/CD
- Use Docker secrets or a cloud secrets manager
- Add Kubernetes deployment manifests
- Add production-ready Gunicorn/Nginx configuration
This project follows concepts covered in a Docker-focused DevOps learning path, including:
- Docker fundamentals
- Docker commands
- Dockerfiles
- Docker networking
- Docker volumes
- Docker Compose
- Healthchecks
- Multi-stage builds
- Nginx reverse proxy
- Application containerization
- Database containers
- Docker image security
- Docker Scout
The source material referenced for this project is the Docker In One Shot learning guide by TrainWithShubham.
This project goes beyond simply running:
docker runIt demonstrates a realistic application architecture:
Application
+
Database
+
Reverse Proxy
+
Container Networking
+
Persistent Storage
+
Healthchecks
+
Environment Configuration
+
Security Scanning
That makes it a useful portfolio project for demonstrating practical Docker and DevOps fundamentals.
This project is based on the following tutorial by Train with Shubham:
https://youtu.be/9bSbNNH4Nqw
Creator: Train with Shubham
## π Connect With Me
πΌ **LinkedIn:**
https://www.linkedin.com/in/hritikranjan1/
π **Website:**
https://hritikranjan.in
π **DevOps Blogs:**
https://blogs.hritikranjan.in
π» **GitHub:**
https://github.com/hritikranjan1
---
<p align="center">
π <b>Built with Docker β’ Django β’ MySQL β’ Nginx β’ Jenkins</b> π
</p>
<p align="center">
β If you found this project useful, please consider giving the repository a Star!
</p>


