Deploy Django, Gunicorn, NGINX, Postgresql using Docker
Aug 09, 2016 · 4 Min Read · 15 Likes · 67 Comments
This post is based on this article on docker documentation.
I will be extending this post by serving django+gunicorn using Nginx, also I will using Postgresql docker container to use it as database.
Steps
Let us checkout to the following steps.
Create django project
Let’s make an empty directory named myproject and add another folder inside name it src. src should contain the django project. For testing purpose lets put a simple django project inside named mydjango.
Add ‘requirement.pip’
Let’s create a subdirectory inside myproject and name it config. Lets put a requirement.pip
file inside config and write these line in it:
Django==1.10
gunicorn==19.6.0
psycopg2==2.6.2
Create ‘Dockerfile’
Now let’s make a Dockerfile
inside the myproject. This should contain the following lines:
FROM python:3.6
ENV PYTHONUNBUFFERED 1
RUN mkdir /config
ADD /config/requirements.pip /config/
RUN pip install -r /config/requirements.pip
RUN mkdir /src;
WORKDIR /src
So this Dockerfile starts with a Python 3.5 based image. Then the container is modified by adding the requirement.pip
file in /config directory within the container and installing the packages from it.
Install ‘docker-compose’
Let’s create a file called docker-compose.yml
in myproject directory.
The docker-compose.yml
file describes the services that make your app. Here we need a web service(Django+Gunicorn), A database(Postgres), and Proxy Server(Nginx). It also describes which Docker images these services will use, how they will link together, any volumes they might need mounted inside the containers. Finally, the docker-compose.yml
file describes which ports these services expose. See the docker-compose.yml reference for more information on how this file works. Don’t forget to add docker-compose to your python environment by running pip install docker-compose
.
Add configuration to ‘docker-compose.yml’
Let’s add the following configuration to the docker-compose.yml
file:
version: "2"
services:
nginx:
image: nginx:latest
container_name: ng01
ports:
- "8000:8000"
volumes:
- ./src:/src
- ./config/nginx:/etc/nginx/conf.d
depends_on:
- web
web:
build: .
container_name: dg01
command: bash -c "python manage.py makemigrations && python manage.py migrate && gunicorn mydjango.wsgi -b 0.0.0.0:8000"
depends_on:
- db
volumes:
- ./src:/src
expose:
- "8000"
db:
image: postgres:latest
container_name: ps01
It says that there are three services for this project: nginx, web, db. nginx depends on web, web depends on db. db container uses postgres’s latest image from dockerhub. Default username for db is postgres
and password is postgres
web container is build using project’s Dockerfile
. It mounts src directory into it and exposes port 8000. version
is being used for which format to use to compose the docker file.
nginx uses nginx’s latest image from dockerhub. This proxy server is accessible from port 8000. It mounts src and config directory.
Create NGINX config
Now let’s write a nginx configuration config file named mydjango.conf
inside myproject’s config folder and put it in a subdirectory named nginx.
upstream web {
ip_hash;
server web:8000;
}
# portal
server {
location / {
proxy_pass http://web/;
}
listen 8000;
server_name localhost;
}
So what it does that, nginx acts as a reverse proxy for any connections going to django server and all connections goes through nginx to reach django server.
Project Directory should look like this:
── myproject
├── src
│ ├── mydjango
│ ├── manage.py
├── config
│ ├── requirements.pip
│ ├── nginx
│ ├── mydjango.conf
├── Dockerfile
└── docker-compose.yml
Database configuration
To communicate from django to postgres, we need to put database configuration in django applications settings file. It should look like this:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'postgres',
'USER': 'postgres',
'HOST': 'db',
'PORT': 5432,
}
}
Almost done
All is done. Now lets run docker-compose build
in terminal within the project directory. It will build/rebuild(if necessary) all the containers. For first time running the containers, run docker-compose up -d
. Lets go to browser and type: localhost:8000
. We should see the django application up and running.
Start and stop docker compose
For stopping the docker, run docker-compose stop
. Re-running docker, use docker-compose start
.
Shell access
For shell accessing.
#Nginx
docker exec -ti nginx bash
#Web
docker exec -ti web bash
#Database
docker exec -ti db bash
For logs:
#Nginx
docker-compose logs nginx
#Web
docker-compose logs web
#DB
docker-compose logs db
Thats it.
Example source code
You can see an working example here in my repo: https://github.com/ruddra/docker-django
Also another deployment example for Ruby on rails here: https://github.com/ruddra/deploy-notebook (Thanks to Akimul Islam for the source)
In conclusion
Thats all for now. Thanks for reading. If you have any question, please post it in comment section below.
Cheers!!
Update
Serving django with gunicorn won’t allow you to serve static files with it. You need to serve static files seperately. You can follow this post for how to do serve static files using Nginx from docker.
Last updated: May 04, 2025
i have tried the your by cloning it from from repo . on gunicron –bind my i am putting my server ip . its not working .please help
i have tried the example by cloneing the project directly on server but on (gunicorn –bind) i have replaced the localhost with my server ip .but its showing “invalid address” error . i have attached the screenshot ,please check and help !! https://uploads.disquscdn.c…
Hi, thank you for mentioning the issue. I have updated the master branch of the github repo with a fix, it should solve the issue. 😄 Hope it helps.
Sir can you provide some tutorials on saltstack ?
I am not really familiar with saltstack. But if I get familiar, I will do some tutorials upon it.
https://uploads.disquscdn.c… https://uploads.disquscdn.c…
Hi Sidhartha, Sorry for late response. You should not replace the server and upstream name. You should keep it as it is said in the tutorial. 😄
In your docker file 4th step is giving error.Please change it from
ADD requirements.pip /config/
toADD config/requirements.pip /config/
Thanks for noticing. Updated post 😄 .
I have tried the latest code . I have only replaced the “src” with my django project . As i am trying host it on digital ocean server i have replaced “gunicorn bind " with my server ip .but it is giving error and i have changed the server name to my digital ocean ip address in nginx configuration file . Please help !! https://uploads.disquscdn.c… https://uploads.disquscdn.c…
Awesome Tutorial, but I can’t reach my web page. On the web logs It seems that Gunicorn is working normally.
django01 | [2017-01-16 00:40:22 +0000] [9] [INFO] Starting gunicorn 19.6.0 django01 | [2017-01-16 00:40:22 +0000] [9] [INFO] Listening at: http://0.0.0.0:8000 (9) django01 | [2017-01-16 00:40:22 +0000] [9] [INFO] Using worker: sync django01 | [2017-01-16 00:40:23 +0000] [12] [INFO] Booting worker with pid: 12
But my Nginx logs only says: Attaching to nginx01 I didn’t create a /config/ folder, just created a django.conf file and in my docker-compose.yml I changed volumes to: volumes:- ./src:/src - ./nginx:/etc/nginx/conf.d
Don’t know whats wrong 😞Well, just changed my structure just to follow yours, and It worked! Thanks, I’ll try to see what was wrong with my structure. Now I’m getting a 502 Bad Gateway error, I’ll try to figure it out.
Me again- put my container IP on
allowed-hosts
insettings.py
, and It’s done!hello, good stuff indeed, but I face problems deploying the very same thing on another server. What changes do you think need to be done to the nginx.conf file for the same?
What parts of the setup need to change so that nGinx is available on port 80 to the “outside world” but gunicorn still keeps using port 8000?
You need to change in `server` section
Cool!! 😄
My requirement.txt is containing lots of libraries ,So while creating the virtualenv most of the time it gives error,So “RUN pip install -r /config/requirements.pip " this step fails for me .how to resolve ?
I haven’t faced this issue yet. For now, I think you can split the requirements.pip into multiple files and run them one after another. Hope that will resolve the issue.
Thanks alot for you post!
Another Django + PostgreSQL + Nginx + Redis + Celery + WebSocket server example
Thank you for the post! The tutorial might be changed(Apr.2017) since you wrote this post: https://docs.docker.com/compose/django/. I have made it work reading your post. Here is my repo: https://github.com/wataruoguchi/setup_app/tree/master/docker_compose … Again, thank you so much!
Thanks a lot for this post, it´s much better than the official one, I am totally new on this and I have a question (maybe it´s a silly question). When you setup Django with PostgreSQL each one has to be on it´s own server or virtual machine, I am trying to setup this but I am getting errors:
could not connect to server Connection refused web_1 | Is the server running on host 'localhost' (127.0.0.1) and accepting web_1 | TCP/IP connections on port 5432?
I finally made it work!, thanks again!, I have just a little problem, how can I run the migrations? because if I write makemigrations without docker (just
python manage.py makemigrations
) I am getting an error because `‘HOST’ ‘db’ is not foundHi, sorry I couldn’t reply on your earlier comment. Anyways, you have go to the docker’s shell and run this command. Run `docker exec -ti web bash` and go to the directory containing manage.py and run the migration commands. Hope it helps
Thank you very much Ruddra!
Hello Ruddra, I´ve been working only with Django and Postgress so far, I followed your tutorial before and it helped me a lot, now I am about to integrate gunicorn and Nginx, but I noticed your docker-compose file uses version: 2, is there a difference if I am using version 3? I guess the part that says
bash -c 'python manage.py makemigrations && python manage.py migrate && gunicorn mydjango.wsgi -b 0.0.0.0:8000'
. I am going to get it from theDOCKERFILE
through an image, and I have seen in some tutorials they use a file called entrypoint (which I don´t really understand but can learn if this is the right path). Another question is, I have been looking and comparing different tutorials, and sometimes in the DOCKERFILE I see from Ubuntu 14.02 (or any version) and sometimes I see from Python 3.6 (or any version), why is this? I am really new at this so excuse me if this is a silly questionHello there. I kind have this working. In your Dockerfile you just need insert a CMD command, just like this:
CMD ["gunicorn", "app.wsgi:application", "--name", "app", "--workers", "3", "--user=www-data", "--group=www-data", "--log-level=debug", "--bind=unix:/var/www/app/run/gunicorn.sock"\]
and you have your gunicorn runing inside your app-container. Remember that your Dockerfile can have only one CMD command. The “location” in your nginx is the gunicorn.socklocation / { include proxy_params; proxy_pass http://unix:/var/www/app/run/gunicorn.sock; }
Thanks Salomão, let me check if I understood, the bind=unix:/var/www/app/run/gunicorn.sock binds the connection to an url which is inside the gunicorn.sock file and I have to have a reference to it from nginx config location? What is this gunicorn.sock, in the official web page http://gunicorn-docs.readth… I see this:
upstream app_server { server unix:/tmp/gunicorn.sock fail_timeout=0; # For a TCP configuration: # server 192.168.0.7:8000 fail_timeout=0; }
Thanks again for your responseDockerFile?
I getting this err: python: can’t open file manage.py’: [Errno 2] No such file or directory
full output: ttaching to TWVpsql, TWVdjango, TWVnginx WVpsql | LOG: database system was shut down at 2017-08-08 07:29:00 UTC WVdjango | python: can't open file manage.py': [Errno 2] No such file or directory WVpsql | LOG: MultiXact member wraparound protections are now enabled WVpsql | LOG: database system is ready to accept connections WVdjango exited with code 2 WVnginx | 2017/08/08 07:29:15 [emerg] 1#1: host not found in upstream 'web:8000' in /etc/nginx/conf.d/TWV.conf:3 WVnginx | nginx: [emerg] host not found in upstream 'web:8000' in /etc/nginx/conf.d/TWV.conf:3 WVnginx exited with code 1
do you have a manage.py in src/ If that’s absent you don’t have a correct django app in src/
Great tutorial, thanks!
Hi, Thanks for these instructions. I was able to run my web locally using Docker Terminal on Windows with ‘docker-compose build’ & ‘docker-compose-up’, However, After creating the image, (Image created Successfully) I am not able to run the web from the image itself I used docker run -p [newport]:8000 [image], looks to me the container immediately exists after executing, when I try to browse the new port nothing is there .. I did netstat -t , no binding is there to the new port… Any idea whats wrong? I am new to Docker.
Hi there, which is your advice to differentiate the local.py environment with the production.py enviroment? (i.e different credentials for the database?). Thanks!
I personally use this:
try: import * from .local_settings except ImportError: pass
local_settings.py can be put in gitignore. You can put your DB configs and other server related stuff there.Try putting the container name in instead, using
ALLOWED_HOSTS = ['web']
worked for me!Anybody has such problem?
ModuleNotFoundError: No module named 'utility'
Thanks for the tutorial! My nginx produces no logs at all and cannot connect to it using a browser. Anyone else having this?
Great tutorial! Only thing you missed to explain is setting
ALLOWED_HOSTS = ['web']
to get your app running.Its fixed now. Please pull the latest code.
I am using docker 3 with django 2.0.3 and I have my static files within a static folder in my app. I also have my html files in a template folder within the app directory. I used your code here as well as in your static file serving tutorial, however, I cannot seem to get the static files to be served using your code. Also using your code, I cannot seem to have nginx run as it says the port has already been allocated. When I change the port, it just says bad gateway. Help please.
Its hard to say anything. Its better if you check the logs and pinpoint the bugs. Also check the code in config>mydjango.conf in given repository with this Blog.
My nginx config is the same as the one you have in the repository. As for the logs, everything seems fine. The only thing that is out of place is when it says static not found: then uri given.
You need to use collectstatic and put the static files in static directory. After that, you need to restart your server
Yes. That is what I did. And the errors I received are the result. I had put the colletcstatic command in the bash commands in the docker-compose file, so the command runs every time I do docker-compose up –build
Here is my docker-compose.yml file: https://pastebin.com/mtqL15TZ
Here is my docker-compose file: https://pastebin.com/h2K2p5Wn And my nginx config file: https://pastebin.com/Ln8K3LHH
There are several things wrong here. In django, you are running the app in 8000 port, but in Nginx, you are using web:8001, which should not work. Remove static volume from docker-compose nginx service. Also for debugging purpose, access shell of both web and nginx container. Go to static directory and run `ls` to check if static files exists or not.
Alright, i returned the nginx port to 8000 as you say, however, I keep getting this error: https://pastebin.com/8xwAxzbb, I cannot login to the containers shell for I cannot get past the error.
Okay, i returned the nginx port to 8000 as you say, however, I keep getting this error: https://pastebin.com/8xwAxzbb. I cannot login to the containers shell for I cannot get past the error.
“Bind for 0.0.0.0:8000 failed: port is already allocated” - This is quite self explanatory. 8000 port is being used in another program. I am sorry, but you need to find and solve this issue yourself.
If it’s not working as is, check in his git repo the nginx conf file and copy it to your files, that made it work for me.
instead of
command: bash -c "python manage.py makemigrations && python manage.py migrate && gunicorn mydjango.wsgi -b 0.0.0.0:8000"
the following line is correct.command: bash -c "python manage.py makemigrations && python manage.py migrate && gunicorn myprojet.wsgi -b 0.0.0.0:8000"
Congratulations for this tutorial! really interesting
Thanks for the awesome 3x blogs / tutorials. I am trying to get the package from ytour github up and running. “make build” and “make up” appear to work without error. However visiting localhost:8000 gives me an nginx 502 bad gateway. Shell-DB and Shell-Nginx gets me on no problem. “make shell-web” gives “Error response from daemon: Container 3875b0c01522e8736fbb990f9f62c8b3447d5990f013b6969bac5d5a1cca6648 is not running” Any advice where to point me? Apologies for the question… its my first time playing with containers… i am excited to get my django project containorised.
this is my
docker ps
: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 032e46e131f2 nginx:latest “nginx -g “daemon of…” 16 minutes ago Up 12 minutes 80/tcp, 0.0.0.0:8000->8000/tcp nz01 9d7049646279 redis:latest “docker-entrypoint.s…” About an hour ago Up 12 minutes 0.0.0.0:6379->6379/tcp rz01 2f71f22010e7 postgres:latest “docker-entrypoint.s…” About an hour ago Up 12 minutes 5432/tcp pz01I have used kitematic to have a look at dz01… this is whats on there: https://pastebin.com/WhENnXn6
FIXED IT… for anyone else that gets here, there was an issue with celery… updating the requirements file from 4.0.0 to 4.1.1 fixed it
Thanks. I will put the fix in the repository 😄
Hi, I’ve tried to follow your tutorial and successfully build the docker project.
$ docker-compose start Starting db ... done Starting web ... done Starting nginx ... done
But in docker ps I only got this: cccd210ee314 postgres:latest ‘docker-entrypoint.s…’ 4 minutes ago Up 8seconds 5432/tcp ps01 Seems like only my postgres db running, and localhost:8000 is not workingmost probably its for psycopg2. Please use 4.1.1 or greater.
Since this method does not check for the domain is it subseptible to Cross-Site Request Forgery? https://docs.djangoproject.com/en/2.0/topics/security/#host-header-validation
You can put your preferred domains in ALLOWED_HOSTS or you can look into this package as well: https://github.com/ottoyiu/django-cors-headers
the app was not able to connect to db until I renamed the containers: web, nginx and db.
This tutorial is great - was very useful. Has anyone got any resources for deploying this setup to AWS…. I have never done that myself. Would be cool to have a bit of a walk-through if anyone knows of a decent one.