Django Docker Deployment with HTTPS using Letsencrypt
In this guide, I’ll show you how to enable HTTPS on a Django app that’s deployed using Docker.
You can find the full source code for this tutorial project here: LondonAppDeveloper/django-docker-deployment-with-https-using-letsencrypt
Prerequisites
In order to follow this guide, you’ll need the following:
- Docker Desktop or Docker and Docker Compose installed using preferred method
- VSCode or a preferred code editor
Setup Docker
The first step is to add Docker to our project so we can use it to create our Django app.
Start by creating a requirements.txt file which will be used to list all the Python requirements our project needs:
Django==4.0.5
uWSGI==2.0.20
Create a Dockerfile which will be used for our Django application.
FROM python:3.10-alpine3.16
ENV PYTHONUNBUFFERED 1
COPY requirements.txt /requirements.txt
RUN apk add --upgrade --no-cache build-base linux-headers && \
pip install --upgrade pip && \
pip install -r /requirements.txt
COPY app/ /app
WORKDIR /app
RUN adduser --disabled-password --no-create-home django
USER django
CMD ["uwsgi", "--socket", ":9000", "--workers", "4", "--master", "--enable-threads", "--module", "app.wsgi"]
Create docker-compose.yml which will be used as our development docker service configuration.
version: '3.9'
services:
app:
build:
context: .
command: sh -c "python manage.py runserver 0.0.0.0:8000"
volumes:
- ./app:/app
ports:
- 8000:8000
We’ll put our Django project in a directory called app/
in the root of our project. We need to create an empty directory now in order to build our Dockerfile which will be used to run the Django CLI.
Create our Django project
Now that Docker is setup, we can create our Django project by running the following:
docker-compose run --rm app sh -c "django-admin startproject app ."
This will create a new Django project in our app directory.
Now run docker-compose up
to start the development server locally.
Once done, you should be able to navigate to http://127.0.0.1:8000 and see the Django launch page.
Create Django app
Next we’ll configure Django and create an app which we can use to test our deployed code.
Run the following command to create a new app:
docker-compose run --rm app sh -c "python manage.py startapp home"
Since we’re creating a simple app for testing, you can remove the following boilerplate files from our new app:
- migrations/
- admin.py
- models.py
- tests.py
Open settings.py and add the new home
app to the INSTALLED_APPS
list:
INSTALLED_APPS = [
...
'home',
]
Create a new file (and subdirectories) at app/home/templates/home/index.html and add the following contents:
<html>
<head>
<title>Django with HTTPS</title>
</head>
<body>
<h1>Hello!</h1>
<p>This is a Django app with HTTPS enabled!</p>
</body>
</html>
Update views.py to look like this:
from django.shortcuts import render
def index(request):
return render(request, "home/index.html")
Now we need to wire this view up to a URL. Do this by editing app/app/urls.py to look like this:
from django.contrib import admin
from django.urls import path
from home import views
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.index),
]
Now if you refresh the page, it should look like this:
Of course, HTTPS isn’t actually enabled yet – that’s what we’ll do in the rest of this tutorial.
Add NGINX Docker Image
The next step is to add an NGINX reverse proxy to the project.
The first time we run it, it will handle the following initialisation steps:
- Generate DH Parameters which will be stored in a volume (see What’s the purpose of DH Parameters? to learn what this is for)
- Handle the acme challenge of HTTP for initialising our certificate
These are only required the first time we deploy our project to a new server.
After the first run, it will then handle the following:
- Redirect HTTP requests to HTTPS
- Handle Django static files
- Forward requests to uWSGI
Because our NGINX image will have numerous configuration files, we’ll create a new subdirectory for them at docker/proxy/.
First we’ll create our default config template at docker/proxy/nginx/default.conf.tpl:
server {
listen 80;
server_name ${DOMAIN} www.${DOMAIN};
location /.well-known/acme-challenge/ {
root /vol/www/;
}
location / {
return 301 https://$host$request_uri;
}
}
This will be the first configuration file used to handle the initial setup of our project.
We put .tpl at the end of the file name because this is a template file which we’ll use to generate our actual config at runtime. This is so we can replace all instances of ${DOMAIN}
with the domain name we want to use for our project.
From the top down, this is what the file does:
- Sets up a server block which is required to define our NGINX server configuration
- Listen on port 80 – the default port for HTTP
- Add a location block for
/.well-known/acme-challenge/
that serves data from/vol/www/
– this will serve a one time password generated by the certbot which needs to be accessible on the internet for letsencrypt to give us a certificate (see Challenge Types for more details) - Redirect all other requests to https
Now create a second file at docker/proxy/nginx/default-ssl.conf.tpl and add the following:
server {
listen 80;
server_name ${DOMAIN} www.${DOMAIN};
location /.well-known/acme-challenge/ {
root /vol/www/;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
server_name ${DOMAIN} www.${DOMAIN};
ssl_certificate /etc/letsencrypt/live/${DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}/privkey.pem;
include /etc/nginx/options-ssl-nginx.conf;
ssl_dhparam /vol/proxy/ssl-dhparams.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location /static {
alias /vol/static;
}
location / {
uwsgi_pass ${APP_HOST}:${APP_PORT};
include /etc/nginx/uwsgi_params;
client_max_body_size 10M;
}
}
This is the config which will be used once our SSL/TLS certificates are initialised.
The first block is the same as the default.conf.tpl file we created previously. This is so we can continue to redirect HTTP to HTTPS and handle the acme challenge for certificate renewals.
The second server block does the following:
- Listens on port 443 with SSL
- Sets the server name as configured by the
DOMAIN
variable - Configure our
ssl_certificate
andssl_certificate_key
which will be set by certbot and mapped to/etc/letsencrypt/
via a volume later on - Include our options-ssl-nginx.conf file which we’ll add in a minute
- Set our
ssl_dhparam
file which we’ll be adding to our startup script later on - Adds a header which enables
Strict-Transport-Security
which is a way to tell the client’s browser to always use HTTPS for our domain and subdomains - Handles requests to
/static
and serves them from the /vol/static directory – this is a way to handle Django static files (we don’t cover that in this guide, but you may want to add it later) - Adds a location block for
/
which will take the rest of the requests and forward them to our uWSGI service running onAPP_HOST
andAPP_PORT
Add a new file at docker/proxy/nginx/options-ssl-nginx.conf with the following contents:
# Taken from:
# https://github.com/certbot/certbot/blob/1.28.0/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf
ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_timeout 1440m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
As the comment at the top of the file suggests, which is taken from the official certbot configuration. It contains the configuration options that are needed to use SSL/TLS certificates retrieved by certbot.
Create a new file at docker/proxy/nginx/uwsgi_params and add the following contents:
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_ADDR $server_addr;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
This file is taken from the official uWSGI docs. It’s used to pass header parameters from NGINX to our Django app.
Add a new file called docker/proxy/run.sh with the following content:
#!/bin/bash
set -e
echo "Checking for dhparams.pem"
if [ ! -f "/vol/proxy/ssl-dhparams.pem" ]; then
echo "dhparams.pem does not exist - creating it"
openssl dhparam -out /vol/proxy/ssl-dhparams.pem 2048
fi
# Avoid replacing these with envsubst
export host=\$host
export request_uri=\$request_uri
echo "Checking for fullchain.pem"
if [ ! -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]; then
echo "No SSL cert, enabling HTTP only..."
envsubst < /etc/nginx/default.conf.tpl > /etc/nginx/conf.d/default.conf
else
echo "SSL cert exists, enabling HTTPS..."
envsubst < /etc/nginx/default-ssl.conf.tpl > /etc/nginx/conf.d/default.conf
fi
nginx -g 'daemon off;'
This is a bash script which does the following:
- Check if
/vol/proxy/ssl-dhparams.pem
exists, if not then run openssl dhparam to generate it – this is required the first time we run the proxy - Check if
/etc/letsencrypt/live/${DOMAIN}/fullchain.pem
exists, if not then copy the default.conf.tpl – this will cause the server to run without SSL so it can serve the acme challenge - Set
host
andrequire_uri
variables to prevent them being overwritten with blank values in the configs - If the
fullchain.pem
file does exist, copy the default-ssl.conf.tpl – this will cause the server to enable SSL
Now we can create docker/nginx/Dockerfile and add the following:
FROM nginx:1.23.0-alpine
COPY ./nginx/* /etc/nginx/
COPY ./run.sh /run.sh
ENV APP_HOST=app
ENV APP_PORT=9000
USER root
RUN apk add --no-cache openssl bash
RUN chmod +x /run.sh
VOLUME /vol/static
VOLUME /vol/www
CMD ["/run.sh"]
This will build an image with the following:
- Based on the nginx image
- Copies the config files and scripts from our project folder to the docker image
- Set default configuration values for
APP_HOST
andAPP_PORT
which are used to configure which uWSGI service requests will be forwarded too - Install openssl which is required to generate the dh params and bash which is used to run our
run.sh
script - Make our run.sh file executable
- Define two values: /vol/static which can be used to map static files from our Django app to the proxy, and /vol/www which will serve our acme challenge
- Set the command to /run.sh so we don’t need to specify it when running containers from our image
Add certbot Docker Image
Next we are going to add our certbot Docker image which will be used to retrieve our first certificate and then handle renewals.
Create a file (and directory) at docker/certbot/certify-init.sh, and add the following contents:
#!/bin/sh
# Waits for proxy to be available, then gets the first certificate.
set -e
# Use netcat (nc) to check port 80, and keep checking every 5 seconds
# until it is available. This is so nginx has time to start before
# certbot runs.
until nc -z proxy 80; do
echo "Waiting for proxy..."
sleep 5s & wait ${!}
done
echo "Getting certificate..."
certbot certonly \
--webroot \
--webroot-path "/vol/www/" \
-d "$DOMAIN" \
--email $EMAIL \
--rsa-key-size 4096 \
--agree-tos \
--noninteractive
See the inline comments for what the script does.
The certbot certonly
command is how we run certbot and tell it to get us a certificate. The flags do the following:
certonly
means get the certificate only (don’t try and install it into the web server)--webroot
tells it to obtain a certificate by writing to the wrbroot directory of an already running server--webroot-path
is used to specify the path of the webroot-d
is used to specify the domain we want to get the certificate for – we’ll set the value as an environment variable later--email
needs to be set to a valid email address for renewal to work – we’ll also set this as an environment variable later--rsa-key-size
is the size of the rsa key (4096 is better than 2048)--agree-tos
confirms that we agree to the Let’s Encrypt Subscriber Agreement--noninteractive
tells certbot we are running this as a script, so we don’t want it to prompt for any inputs
Now we’ll add our Dockerfile for certbot.
Create a new file at docker/certbot/Dockerfile and add the following:
FROM certbot/certbot:v1.27.0
COPY certify-init.sh /opt/
RUN chmod +x /opt/certify-init.sh
ENTRYPOINT []
CMD ["certbot", "renew"]
This Dockerfile
does the following:
- Bases our image from certbot so we can get the certbot executable as well as netcat
- Adds our certify-init.sh script which we created above
- Sets the entrypoint to an empty array – this is to override the default certbot entrypoint so we can run our script easier
- Set the default command to
certbot renew
Add deployment Docker Compose
Next we’ll add a specific Docker Compose configuration that will be used for deployment.
Open app/app/settings.py and change it to this:
import os
# ...
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", "setmeinprod")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = bool(int(os.environ.get("DJANGO_DEBUG", 0)))
ALLOWED_HOSTS = [] if DEBUG else os.environ.get("DJANGO_ALLOWED_HOSTS").split(',')
# ...
The purpose of this change is to make some of our settings configurable from environment variables which we can set in our Docker Compose config.
Specifically we do the following:
- Import
os
which is the build-in library that’s used to retrieve values from environment variables - Pull the
SECRET_KEY
value from theDJANGO_SECRET_KEY
setting so we can avoid keeping the real secret key in our code - Set the
DEBUG
setting fromDJANGO_DEBUG
– because environment variables come in as strings, we need to confirm it to an integer and then a bool (so1
=True
and0
=False
) - Set
ALLOWED_HOSTS
fromDJANGO_ALLOWED_HOSTS
which will be used to set our custom domain name
Update docker-compose.yml to add an environment variable to enable debug mode, so it looks like this:
version: '3.9'
services:
app:
build:
context: .
command: sh -c "python manage.py runserver 0.0.0.0:8000"
volumes:
- ./app:/app
ports:
- 8000:8000
environment:
- DJANGO_DEBUG=1
Now create a new file in the root of the project called docker-compose.deploy.yml with the following contents:
version: "3.9"
services:
app:
build:
context: .
restart: always
environment:
- DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}
- DJANGO_ALLOWED_HOSTS=${DOMAIN}
proxy:
build:
context: ./docker/proxy
restart: always
depends_on:
- app
ports:
- 80:80
- 443:443
volumes:
- certbot-web:/vol/www
- proxy-dhparams:/vol/proxy
- certbot-certs:/etc/letsencrypt
environment:
- DOMAIN=${DOMAIN}
certbot:
build:
context: ./docker/certbot
command: echo "Skipping..."
environment:
- EMAIL=${ACME_DEFAULT_EMAIL}
- DOMAIN=${DOMAIN}
volumes:
- certbot-web:/vol/www
- certbot-certs:/etc/letsencrypt/
depends_on:
- proxy
volumes:
certbot-web:
proxy-dhparams:
certbot-certs:
This defines our Docker Compose deployment with the services:
- An
app
service which will run our Django app - A
proxy
service which runs our reserve proxy and opens port 80 (HTTP) and 443 (HTTPS) - A
certbot
service which will handle retrieving and renewing our certificate
We also create the following volumes:
- certbot-web – this is used to share that acme challenge with our nginx web server, so it can be made accessible to fulfil the request.
- proxy-dhparams – this is used to store our dhparams file which is generated by our proxy the first time we run it.
- certbot-certs – this will hold our certificates that are generated by certbot, and make them available to our nginx proxy.
As you see we pull in some environment variables using the ${VAR_NAME} syntax in our config. This can be used to pull values from a file called .env which we will store on our server.
We’ll create a sample of the file so we can easily copy it and update the values at deploy time.
Create a new file called .env.sample and add the following contents:
DJANGO_SECRET_KEY=secretkey123
ACME_DEFAULT_EMAIL=email@example.com
DOMAIN=example.com
The above file is just a template with values which we must change upon deployment.
Create a server
Next we need to create a new server to run our service.
Our server will require the following:
- Accessible via SSH (22), HTTP (80) and HTTPS (443)
- Has Docker and Docker Compose installed
- Has crontab – used to automate renewal
I’ll be using AWS in the steps below, but feel free to use whatever host you want as you can create a server with the following specs.
First, login to the AWS console at https://console.aws.amazon.com/
We’ll be using SSH to connect to our server, so before we create it, navigate to EC2 > Key Pairs, and import your public key for SSH authentication (teaching SSH is out of scope for this tutorial).
Next, head back to the EC2 Dashboard and select Launch Instance:
On the Launch an instance page, enter the following:
- Name: django-https (or whatever name you prefer)
- Application and OS Images: Amazon Linux 2 AMI (HVM)
- Architecture: 64-bit (x86)
- Instance type: t2.micro – this should be enough for this demo, but you may want to use a larger machine for a real deployment
- Key pair name: Select your key pair for SSH auth
- Network settings: Allow SSH, HTTP and HTTPS traffic
- Configure storage: I recommend at least 25GB as Docker needs to pull a bunch of base images
Note that AWS may charge you for creating this virtual machine. It’s your responsibility to review and accept all costs associated with following this guide. If in doubt, see the AWS official documentation or contact their support.
Now select Launch instance. Then choose View all instances to view the newly created instance.
After the instance is launched, you should be able to select it and view it’s Public IPv4 DNS:
Use the public IPv4 DNS to connect to the server via SSH:
ssh ec2-user@<address>
After you’ve connected, install Docker with the following command:
# Install Docker
sudo yum update -y
sudo amazon-linux-extras install -y docker
sudo systemctl enable docker.service
sudo systemctl start docker.service
sudo usermod -aG docker ec2-user
# Install Docker Compose
wget https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)
sudo mv docker-compose-$(uname -s)-$(uname -m) /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# Install Git
sudo yum install -y git
This does the following:
- Updates the yum package manager repos
- Installs docker
- Enables the docker service so it auto starts
- Starts the docker setvice
- Adds the ec2-user to the docker group so it can run containers
- Installs docker-compose
- Installs Git which we’ll need to clone our project
To confirm that everything is installed correctly, run:
[ec2-user@ip-172-31-40-166 ~]$ docker --version
Docker version 20.10.13, build a224086
[ec2-user@ip-172-31-40-166 ~]$ docker-compose --version
docker-compose version 1.29.2, build unknown
Then, type exit to disconnect from SSH, and then reconnect again (this is so the group change gets applied to the user)
Now run docker run hello-world
to ensure you can run containers. If it worked, it should look something like this:
[ec2-user@ip-172-31-40-166 ~]$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:13e367d31ae85359f42d637adf6da428f76d75dc9afeb3c21faea0d976f5c651
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
[ec2-user@ip-172-31-40-166 ~]$
Deploying from GitHub
Now we have a server to deploy to, we need to find a way to get our code from GitHub onto the server.
There are various ways to do this. For this guide, we’ll create a special SSH key on our server called a “Deploy Key”, and configure our GitHub project to permit cloning with this key.
Ensure you are connected to your server via SSH and run the following:
ssh-keygen -t ed25519 -C "GitHub Deploy Key"
When prompted for the key path, leave it as default.
When prompted for the passphrase, leave this empty (or set a passphrase that you will remember to use for every deployment).
[ec2-user@ip-172-31-40-166 ~]$ ssh-keygen -t ed25519 -C "GitHub Deploy Key"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/ec2-user/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/ec2-user/.ssh/id_ed25519.
Your public key has been saved in /home/ec2-user/.ssh/id_ed25519.pub.
The key fingerprint is:
SHA256:oC7ffxpnSmIwUWkdsTUwo58+vdw6YTYbhWJKgTJSL68 GitHub Deploy Key
The key's randomart image is:
+--[ED25519 256]--+
| .. ..o*+o |
|. o...+..= . |
| ..oo.o.. . |
| o o.oo.. . |
| =. oS. . |
| o o.. .* |
| E . o =o+= |
| o o o B+o |
| . ..+o+o. |
+----[SHA256]-----+
[ec2-user@ip-172-31-40-166 ~]$
Now run cat ~/.ssh/id_ed25519.pub
to print the public key, and copy it’s contents to your keyboard:
With the public key copied to your keyboard, head over to your GitHub project and choose Settings > Deploy Keys:
Select Add deploy key and give it a title before pasting the contents of your key. Then click Add key.
You may be prompted to re-enter your GitHub password.
After that, your key should be added:
Now go to your Github project’s Code tab, and choose Code > SSH before copying the clone URL:
Now head back to the server and run:
git clone <paste url here>
This will clone your project to your server.
You can now use cd to navigate to the cloned project:
[ec2-user@ip-172-31-40-166 ~]$ ls
django-docker-deployment-with-https-using-letsencrypt
[ec2-user@ip-172-31-40-166 ~]$ cd django-docker-deployment-with-https-using-letsencrypt/
(Yours may look a bit different depending on what you called your Git repo)
Setup DNS
Unfortunately, Let’s Encrypt will not allow you to register a certificate for the ephemeral hostname that AWS provides for new EC2 instances.
As a result, you’ll need to register a custom domain name to access your services.
There are numerous ways to do this, and DNS is a complex topic which is out of scope for this guide.
The easiest option is to register your own domain using Route 53 in AWS, but NOTE that there will be a charge for this (even in the AWS free tier). It’s usually around $10 per year, but it depends on the name you wish to register.
Once you have a Hosted zone in AWS, you can add a new subdomain by creating a CNAME record like below:
Note that the value above is the hostname of my EC2 instance. You should, of course, change this to the hostname of your instance if you want it to work.
The above record will point app.aws.londonapp.dev to the server with the hostname provided in the value field.
Once you hit Create records, it may take a few minutes for the DNS change to propagate and become usable.
Configure app
Now you need to create a configuration file, this is done by copying the .env.sample to .env and changing the values:
cp .env.sample .env
vi .env
Set the values as per your project.
Note, the DOMAIN
value must be the domain name you will use to access your project on because this is used to set your ALLOWED_HOSTS
value.
For example, my config looks like this:
Getting the first certificate
Now the project is cloned, you can run the following command to generate the first SSH certificate:
docker-compose -f docker-compose.deploy.yml run --rm certbot /opt/certify-init.sh
It may take a while to wait for the proxy. This is because the first time we run the service, the proxy will generate our dhparams file, which can take a couple of minutes (this will be saved in a volume so they don’t need to be created again.
If it’s successful, it should look like this:
Now, run the following to stop and start the service:
docker-compose -f docker-compose.deploy.yml down
docker-compose -f docker-compose.deploy.yml up
This will restart all services and serve our application via HTTPS.
Once running, you should be able to navigate to your project at your registered domain name via HTTPS.
Handling renewals
The steps above will create an initial certificate for our project. However, the certificate will only be valid for three months, so you’ll need to run the renew command before then.
To renew, we can run:
docker-compose -f docker-compose.deploy.yml run --rm certbot sh -c "certbot renew"
If you run this now, you should see something like this:
[ec2-user@ip-172-31-36-103 django-docker-deployment-with-https-using-letsencrypt]$ docker-compose -f docker-compose.deploy.yml run --rm certbot sh -c "certbot renew"
Creating django-docker-deployment-with-https-using-letsencrypt_certbot_run ... done
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/app.aws.londonapp.dev.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Certificate not yet due for renewal
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
The following certificates are not due for renewal yet:
/etc/letsencrypt/live/app.aws.londonapp.dev/fullchain.pem expires on 2022-10-01 (skipped)
No renewals were attempted.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
It’s best to automate this using a tool like cron.
In order to do this, create a renewal script in the home directory of your server user (eg: /home/ec2-user/renew.sh) and add the following:
#!/bin/sh
set -e
cd /home/ec2-user/django-docker-deployment-with-https-using-letsencrypt
/usr/local/bin/docker-compose -f docker-compose.deploy.yml run --rm certbot certbot renew
Then, run chmod +x renew.sh
to make it executable. Then run:
crontab -e
Then add the following:
0 0 * * 6 sh /home/ec2-user/renew.sh
This will run the renewal weekly at midnight.
That concludes this guide on how to deploy Django using HTTPS!
Thank you very much for your tutorials, it helps me a lot. I am getting an error related to this topic, but I could not find the problem and I do not understand where I should check.
nc: bad address ‘proxy’
I’m getting the same error, how did you fix this
Hey, I’m having the same issue when I run the script certify-init.sh on my server. How did you end up fixing yours?
Hi Emre, I also had this error, I ‘think’ it’s when nginx configuration in wrong, and in my case that stopped port 80 initialising in certify-init.sh.
This came down to two things , id put the nginx config files in the proxy rather than the proxy/nginx directory.
Also I used a sub-domain on an existing dns (not the aws route 53) and I had a small issue with the configuration of that. Hope this helps and not too late for you.
Hey Simon, I am running into this same issue. Were you able to use your DNS records for your existing DNS or did you end up setting up a new dns record in route 53? I have the same nc: bad address ‘proxy’ error and I would prefer to not use route 53 if possible.
Hi,
Did you solve your problem? I am facing the same issue.
Selam Emre hatanı çözebildin mi? Aynı hatayı ben de alıyorum paylaşırsan sevinirim.
Hi Emre, did you solve your error? I’m getting the same error, I would appreciate if you share.
It’s working , But i got forbidden error when i’m trying to login admin dshboard
Mark,
Thanks for this, came at just the right time for me as I have an existing Django app I need to containerize ( is that a word ?).
Coded along and cleared a few self inflicted bugs, tbh the debugging probably helped embed the knowledge all the more.
Cheers Simon
proxy_1 | Checking for dhparams.pem
proxy_1 | Checking for fullchain.pem
proxy_1 | SSL cert exists, enabling HTTPS…
proxy_1 | 2022/12/01 22:31:45 [emerg] 8#8: PEM_read_bio_DHparams(“/vol/proxy/ssl-dhparams.pem”) failed (SSL: error:0909006C:PEM routines:get_name:no start line:Expecting: DH PARAMETERS)
proxy_1 | nginx: [emerg] PEM_read_bio_DHparams(“/vol/proxy/ssl-dhparams.pem”) failed (SSL: error:0909006C:PEM routines:get_name:no start line:Expecting: DH PARAMETERS)
“Now we can create docker/nginx/Dockerfile and add the following:”
the path should be
“docker/proxy/Dockerfile”
waiting for proxy……
Idk why I am having one specific problem, in the very last step.. I can retrieve the certificate on running the required command for getting it the first time..
But then I do “docker-compose … down”, “docker-compose … up” and I get an error saying “…ssl/dhparams.pem doesn’t exist”.. I inspected the volume and checked that the dhparams.pem file exists on the local machine.. What could I be missing out here?
I get an error just after receiving my certificate, basically I have no vol folder?
So no vol/www for my certificate to go in.
I’ve also tried creating those folders manually but no dice, that doesnt seem to help
Does anyone have any ideas at all? Please?
Hi,
I am using this guide to configure my django project on production. I suspect there is ‘static_root’ missing in your setting.py file. I have tried every method to fix static issue but after collectastatic command I have no errors but static files are not loading. I cannot access admin or swagger due to this issue. I would really appreciate if you can help me with this.
Your work is really helpful.
UZAIR
Great tutorial and thank you very much for this high quality blogpost!
I have the following http/https redirection problem
inside default-ssl.conf.tpl -> return 301 https://$host$request_uri; -> $host and $request_uri are empty (nginx -T also showed the same behavior)
So when I try
http://example.com redirects to https://
if i change $host with ${DOMAIN} then it works but if I try
http://example.com/test redirects to https://example.com
Hi Mrak, really great tutorial. I just want to ask if i want to deploy it on my local machine, can I used DOMAIN=localhost ?
Yes, however you wouldn’t be able to procure a domain certificate for localhost. It has to be a real publicly accessible domain on the internet.