Getting Up and Running Locally With Docker
If you’re new to Docker, please be aware that some resources are cached system-wide and might reappear if you generate a project multiple times with the same name (e.g. this issue with Postgres).
Docker; if you don’t have it yet, follow the installation instructions;
Docker Compose; refer to the official documentation for the installation guide.
Pre-commit; refer to the official documentation for the pre-commit.
Cookiecutter; refer to the official GitHub repository of Cookiecutter
Before Getting Started
Generate a new cookiecutter-django project:
$ cookiecutter gh:cookiecutter/cookiecutter-django
For more information refer to Project Generation Options.
Build the Stack
This can take a while, especially the first time you run this particular command on your development system:
$ docker-compose -f local.yml build
Generally, if you want to emulate production environment use
production.yml instead. And this is true for any other actions you might need to perform: whenever a switch is required, just do it!
Before doing any git commit, pre-commit should be installed globally on your local machine, and then:
$ git init $ pre-commit install
Failing to do so will result with a bunch of CI and Linter errors that can be avoided with pre-commit.
Run the Stack
This brings up both Django and PostgreSQL. The first time it is run it might take a while to get started, but subsequent runs will occur quickly.
Open a terminal at the project root and run the following for local development:
$ docker-compose -f local.yml up
You can also set the environment variable
COMPOSE_FILE pointing to
local.yml like this:
$ export COMPOSE_FILE=local.yml
And then run:
$ docker-compose up
To run in a detached (background) mode, just:
$ docker-compose up -d
Execute Management Commands
As with any shell command that we wish to run in our container, this is done using the
docker-compose -f local.yml run --rm command:
$ docker-compose -f local.yml run --rm django python manage.py migrate $ docker-compose -f local.yml run --rm django python manage.py createsuperuser
django is the target service we are executing the commands against.
(Optionally) Designate your Docker Development Server IP
DEBUG is set to
True, the host is validated against
['localhost', '127.0.0.1', '[::1]']. This is adequate when running a
virtualenv. For Docker, in the
config.settings.local, add your host development server IP to
ALLOWED_HOSTS if the variable exists.
Configuring the Environment
This is the excerpt from your project’s
# ... postgres: build: context: . dockerfile: ./compose/production/postgres/Dockerfile volumes: - local_postgres_data:/var/lib/postgresql/data - local_postgres_data_backups:/backups env_file: - ./.envs/.local/.postgres # ...
The most important thing for us here now is
env_file section enlisting
./.envs/.local/.postgres. Generally, the stack’s behavior is governed by a number of environment variables (env(s), for short) residing in
envs/, for instance, this is what we generate for you:
.envs ├── .local │ ├── .django │ └── .postgres └── .production ├── .django └── .postgres
By convention, for any service
sI in environment
e (you know
someenv is an environment when there is a
someenv.yml file in the project root), given
sI requires configuration, a
.envs/.e/.sI service configuration file exists.
Consider the aforementioned
# PostgreSQL # ------------------------------------------------------------------------------ POSTGRES_HOST=postgres POSTGRES_DB=<your project slug> POSTGRES_USER=XgOWtQtJecsAbaIyslwGvFvPawftNaqO POSTGRES_PASSWORD=jSljDz4whHuwO3aJIgVBrqEml5Ycbghorep4uVJ4xjDYQu0LfuTZdctj7y0YcCLu
The three envs we are presented with here are
POSTGRES_PASSWORD (by the way, their values have also been generated for you). You might have figured out already where these definitions will end up; it’s all the same with
django service container envs.
One final touch: should you ever need to merge
.envs/.production/* in a single
.env run the
$ python merge_production_dotenvs_in_dotenv.py
.env file will then be created, with all your production envs residing beside each other.
Tips & Tricks
Activate a Docker Machine
This tells our computer that all future commands are specifically for the dev1 machine. Using the
eval command we can switch machines as needed.:
$ eval "$(docker-machine env dev1)"
If you are using the following within your code to debug:
import ipdb; ipdb.set_trace()
Then you may need to run the following for it to work as desired:
$ docker-compose -f local.yml run --rm --service-ports django
In order for
django-debug-toolbar to work designate your Docker Machine IP with
container_name from the yml file can be used to check on containers with docker commands, for example:
$ docker logs <project_slug>_local_celeryworker $ docker top <project_slug>_local_celeryworker
Notice that the
container_name is generated dynamically using your project slug as a prefix
When developing locally you can go with MailHog for email testing provided
use_mailhog was set to
y on setup. To proceed,
<project_slug>_local_mailhogcontainer is up and running;
Celery tasks in local development
When not using docker Celery tasks are set to run in Eager mode, so that a full stack is not needed. When using docker the task scheduler will be used by default.
If you need tasks to be executed on the main thread during development set
CELERY_TASK_ALWAYS_EAGER = True in
Possible uses could be for testing, or ease of profiling with DJDT.
Flower is a “real-time monitor and web admin for Celery distributed task queue”.
use_dockerwas set to
yon project initialization;
use_celerywas set to
yon project initialization.
By default, it’s enabled both in local and production environments (
production.yml Docker Compose configs, respectively) through a
flower service. For added security,
flower requires its clients to provide authentication credentials specified as the corresponding environments’
CELERY_FLOWER_PASSWORD environment variables. Check out
localhost:5555 and see for yourself.
Using Webpack or Gulp
When using Webpack or Gulp as the
frontend_pipeline option, you should access your application at the address of the
node service in order to see your correct styles. This is http://localhost:3000 by default. When using any of the other
frontend_pipeline options, you should use the address of the
django service, http://localhost:8000.
Developing locally with HTTPS
Increasingly it is becoming necessary to develop software in a secure environment in order that there are very few changes when deploying to production. Recently Facebook changed their policies for apps/sites that use Facebook login which requires the use of an HTTPS URL for the OAuth redirect URL. So if you want to use the
users application with a OAuth provider such as Facebook, securing your communication to the local development environment will be necessary.
In order to create a secure environment, we need to have a trusted SSL certificate installed in our Docker application.
The official line from Let’s Encrypt is:
[For local development section] … The best option: Generate your own certificate, either self-signed or signed by a local root, and trust it in your operating system’s trust store. Then use that certificate in your local web server. See below for details.
mkcert: Valid Https Certificates For Localhost
mkcert is a simple by design tool that hides all the arcane knowledge required to generate valid TLS certificates. It works for any hostname or IP, including localhost. It supports macOS, Linux, and Windows, and Firefox, Chrome and Java. It even works on mobile devices with a couple manual steps.
After installing a trusted TLS certificate, configure your docker installation. We are going to configure an
nginx reverse-proxy server. This makes sure that it does not interfere with our
traefik configuration that is reserved for production environments.
These are the places that you should configure to secure your local environment.
Take the certificates that you generated and place them in a folder called
certs in the project’s root folder. Assuming that you registered your local hostname as
my-dev-env.local, the certificates you will put in the folder should have the names
... nginx-proxy: image: jwilder/nginx-proxy:alpine container_name: nginx-proxy ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/tmp/docker.sock:ro - ./certs:/etc/nginx/certs restart: always depends_on: - django ...
djangothrough environment variables.
djangoalready has an
.envfile connected to it. Add the following variables. You should do this especially if you are working with a team and you want to keep your local environment details to yourself.
# HTTPS # ------------------------------------------------------------------------------ VIRTUAL_HOST=my-dev-env.local VIRTUAL_PORT=8000
The services run behind the reverse proxy.
You should allow the new hostname.
ALLOWED_HOSTS = ["localhost", "0.0.0.0", "127.0.0.1", "my-dev-env.local"]
$ docker-compose -f local.yml up -d --build
Go to your browser and type in your URL bar
See https with nginx for more information on this configuration.
certs/* to the
.gitignore file. This allows the folder to be included in the repo but its contents to be ignored.
This configuration is for local development environments only. Do not use this for production since you might expose your local