├── app ├── config │ ├── __init__.py │ ├── asgi.py │ ├── wsgi.py │ ├── urls.py │ └── settings.py ├── requirements.txt ├── Dockerfile ├── manage.py └── Dockerfile.prod ├── .dockerignore ├── .gitignore ├── Dockerfile.traefik ├── traefik.dev.toml ├── traefik.prod.toml ├── README.md ├── docker-compose.yml ├── LICENSE └── docker-compose.prod.yml /app/config/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .venv 2 | db.sqlite3 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv 2 | db.sqlite3 -------------------------------------------------------------------------------- /Dockerfile.traefik: -------------------------------------------------------------------------------- 1 | FROM traefik:v2.9.6 2 | 3 | COPY ./traefik.prod.toml ./etc/traefik/traefik.toml 4 | -------------------------------------------------------------------------------- /app/requirements.txt: -------------------------------------------------------------------------------- 1 | Django==4.1.6 2 | django-environ==0.9.0 3 | psycopg2-binary==2.9.5 4 | gunicorn==20.1.0 5 | whitenoise==6.3.0 6 | -------------------------------------------------------------------------------- /traefik.dev.toml: -------------------------------------------------------------------------------- 1 | # listen on port 80 2 | [entryPoints] 3 | [entryPoints.web] 4 | address = ":80" 5 | 6 | # Traefik dashboard over http 7 | [api] 8 | insecure = true 9 | 10 | [log] 11 | level = "DEBUG" 12 | 13 | [accessLog] 14 | 15 | # containers are not discovered automatically 16 | [providers] 17 | [providers.docker] 18 | exposedByDefault = false 19 | -------------------------------------------------------------------------------- /app/Dockerfile: -------------------------------------------------------------------------------- 1 | # app/Dockerfile 2 | 3 | # pull the official docker image 4 | FROM python:3.11.2-slim 5 | 6 | # set work directory 7 | WORKDIR /app 8 | 9 | # set env variables 10 | ENV PYTHONDONTWRITEBYTECODE 1 11 | ENV PYTHONUNBUFFERED 1 12 | 13 | # install dependencies 14 | COPY requirements.txt . 15 | RUN pip install -r requirements.txt 16 | 17 | # copy project 18 | COPY . . 19 | -------------------------------------------------------------------------------- /app/config/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for config project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /app/config/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for config project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /traefik.prod.toml: -------------------------------------------------------------------------------- 1 | [entryPoints] 2 | [entryPoints.web] 3 | address = ":80" 4 | [entryPoints.web.http] 5 | [entryPoints.web.http.redirections] 6 | [entryPoints.web.http.redirections.entryPoint] 7 | to = "websecure" 8 | scheme = "https" 9 | 10 | [entryPoints.websecure] 11 | address = ":443" 12 | 13 | [accessLog] 14 | 15 | [api] 16 | dashboard = true 17 | 18 | [providers] 19 | [providers.docker] 20 | exposedByDefault = false 21 | 22 | [certificatesResolvers.letsencrypt.acme] 23 | email = "your@email.com" 24 | storage = "/certificates/acme.json" 25 | [certificatesResolvers.letsencrypt.acme.httpChallenge] 26 | entryPoint = "web" 27 | -------------------------------------------------------------------------------- /app/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dockerizing Django with Postgres, Gunicorn, and Traefik 2 | 3 | ## Want to learn how to build this? 4 | 5 | Check out the [post](https://testdriven.io/blog/django-docker-traefik/). 6 | 7 | ## Want to use this project? 8 | 9 | ### Development 10 | 11 | Build the images and spin up the containers: 12 | 13 | ```sh 14 | $ docker-compose up -d --build 15 | ``` 16 | 17 | Test it out: 18 | 19 | 1. [http://django.localhost:8008/](http://django.localhost:8008/) 20 | 1. [http://django.localhost:8081/](http://django.localhost:8081/) 21 | 22 | ### Production 23 | 24 | Update the domain in *docker-compose.prod.yml*, and add your email to *traefik.prod.toml*. 25 | 26 | Build the images and run the containers: 27 | 28 | ```sh 29 | $ docker-compose -f docker-compose.prod.yml up -d --build 30 | ``` 31 | -------------------------------------------------------------------------------- /app/config/urls.py: -------------------------------------------------------------------------------- 1 | """config URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/4.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path 18 | 19 | urlpatterns = [ 20 | path("admin/", admin.site.urls), 21 | ] 22 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | web: 5 | build: ./app 6 | command: bash -c 'while !