In this guide, we will deploy Django with Dokku on a VPS with Ubuntu 19.04 installed. Dokku is powered by Docker. Dokku functions a bit like the PaaS solution, Heroku, but in this case you are hosting it on your own VPS. If you don’t have a VPS with at least 1GB of RAM, you will need to use Swap to get it all to work. If you don’t do this, you will get a lot of errors during the process.

If you have used Heroku before, then you know that the incredible easy workflow comes with a cost. In many cases, the benefits outweight the cost, but if you have a very small project, Dokku might just be right for you. Fairly easy to set up and very low cost.

Prerequisites: #

  • A VPS with at least 1GB of RAM running Ubuntu 19.04
  • A Django project that uses Git for version control (in this tutorial we will use the project name: prdjango)

Let’s create and log in as a new user and install a few required libraries:

adduser jane
su - jane
sudo apt-get install -y swig libssl-dev dpkg-dev netcat

Let’s install Dokku #

Download the install file and execute it.

wget https://raw.githubusercontent.com/dokku/dokku/v0.18.2/bootstrap.sh
sudo DOKKU_TAG=v0.18.2 bash bootstrap.sh

You will probably have to enter your password now. The process of installing Dokku can take a few minutes. When creating your server, you get your own IP address (something similar to: 203.0.113.23). Once the installation has been completed, type the IP address in your web browser and you should be welcomed with a setup form from Dokku.

This page will ask you for your public ssh key, but we don’t have that yet. To create such a key, do the following:

ssh-keygen -t rsa

It will ask you immediately where you would like to store the ssh key. If you have never run the previous command before, just press enter. The default one is good enough. If you have run the command before, just change the last part of the path. The next question is to set a passphrase. It’s highly recommended to do this. Please remember this one, as you will have to type it in every time you want to deploy your code on the server.

Once you complete that, you will get some more information about the whereabouts of your keys and the fingerprint. To complete the server setup, we need the path to the public key id_rsa.pub (if you changed the name, it will be different for you). Open the file in a text editor and copy all of its content. Paste the content in the public access part of your website. If the mash of characters starts with ssh-rsa and ends with your PC’s name, then you got it right!

Up next, fill in your IP address in the hostname field and uncheck Use virtualhost naming for apps. Your app URL should look similar to: http://203.0.113.23:<app-port>. Complete the process by clicking on “Finish Setup”. If all went well, you will be redirected to the official Dokku docs. Dokku is now ready to be used.

Prepare your Django project for deployment #

Let’s go back to your Django project. We need to create a requirements.txt file to let Dokku know what packages need to be installed. We can create that based on what packages are currently installed in our environment with this command:

pip freeze >> requirements.txt

Up next, add a new dokku folder in the root of your project. Then add these files in that folder:

CHECK The check file will check if your website still works once you deploy a new version. It will check this right before Dokku pushes your website live. We will leave this file empty for now, Dokku will only run the default checks.

DOKKU_SCALE This file will tell Dokku how many processes it should run. If you only plan to use a web process, then you can simply add web=1 in this file. When you need more processes, you can simply up the number. If you also have async tasks or scheduled tasks in your Django project (like celery) then you will need to add worker=1 for async tasks and beat=1 for scheduled tasks.

Procfile This is the file that will tell Dokku what to run. We will use uwsgi to run this Django project, but you can use gunicorn as well if you want. Our Procfile should look like this (replace prdjango!):

web: /usr/local/bin/uwsgi --chdir=/code --ini=/code/uwsgi.ini
beat: /usr/local/bin/celery beat -A prdjango -linfo
worker: /usr/local/bin/celery -A prdjango worker -P gevent -E --loglevel=info

If you don’t have scheduled celery tasks, you can remove the line starting with beat. If you don’t use celery or any worker process at all, you can remove the line starting with worker as well. The web one is mandatory to get your website up and running.

uwsgi.ini In the previous code block, we refer back to a uwsgi.ini file to run uwsgi. This will have all the configuration options of uwsgi. You can use this one:

[uwsgi]
module=prdjango.wsgi:application
master=True
vacuum=True
max-requests=50
http-socket=:5000
processes=5
harakiri=20000
single-interpreter=True
enable-threads=True
logto=/var/log/uwsgi.log
limit-as=6000

Don’t forget to change prdjango.wsgi to your project’s name.

app.json This is the last file in this folder. This file is optional, but it’s recommended to use it to add extra commands to your deployment process. In this case, we will add a command to run migrations when a new version has been deployed. Here is the config:

{
  "scripts": {
    "dokku": {
      "predeploy": "/code/manage.py migrate"
    }
  }
}

Finally, we need add one more file to the root of your project called ‘Dockerfile`. This file will tell Dokku what to do to deploy a project. Here is an example file of that:

FROM python:latest
ENV PYTHONUNBUFFERED 1

RUN pip install -U pip
ADD requirements.txt /code/
RUN pip install -r /code/requirements.txt uwsgi gevent

ADD dokku/CHECKS /app/
ADD dokku/* /code/
WORKDIR /code
COPY . /code/
RUN /code/manage.py

Create a Dokku project and install dependancies #

We have our Django project ready now, and the last thing to do is prepare our server for this specific project. Jump back to your server and let’s install all Dokku dependencies.

The Postgres database, Redis (skip this if you are not using it for cache or celery) and Let’s Encrypt (skip this if you don’t want to use Let’s Encrypt):

# Postgres    
sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git
# Redis
sudo dokku plugin:install https://github.com/dokku/dokku-redis.git redis
# Let’s encrypt
sudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git

Docker works with containers, and since Dokku is based on Docker, it does that too. We will need to create a container for every separated part. In this case, we will have to create one for the Django project itself, the database, and one for Redis (if you are using that).

Create the Django project container:

sudo dokku apps:create prdjango

Create the Django database container:

sudo dokku postgres:create prdjango-db

Create the Redis container (skip this if you are not using Redis):

sudo dokku redis:create prdjango-redis

Up next, we need to connect all the containers to each other so they can communicate. Dokku containers are, by default, completely isolated.

Link the project to the database:

sudo dokku postgres:link prdjango-db prdjango

Link the project to redis:

sudo dokku redis:link prdjango-redis prdjango

We do not have to link Redis to our Postgres database as they will never directly communicate. This will always be done through your code.

Let’s deploy #

Okay, your server is ready for your project. Let’s put the final touches on your local environment. Go back to your local PC now and run the following:

# make sure you change the IP-address and change the django project name
git remote add dokku dokku@203.0.113.23:prdjango

That’s connects our git repository to the VPS. Up next, let it deploy:

git push dokku master

You will have to use the last command every time when you want to deploy a new version of your app. As you will see, it will ask for the passphrase you entered previously.

The last few lines should look like this:

    =====> Application deployed:
        http://203.0.113.23:64813

If you don’t see any errors, you should have a fully functional website when you enter your IP address (including the port number!) in your web browser. If you do get an error, make sure you added your IP address to your ALLOWED_HOSTS variable in your settings file.

If that didn’t solve it, take a quick look at your log files. You can find them by typing sudo dokku logs prdjango on your server. Again, remember to replace prdjango with your own project name.

Using a domain name instead of an IP address #

Okay. Now, let’s use a domain name for that instead of that ugly and unmemorable IP address.

On your server do:

# Replace yourdomain.com with your actual domain
sudo dokku domains:add prdjango yourdomain.com www.yourdomain.com
# Remove the ip-address. Replace the IP-address with your IP-address
sudo dokku domains:remove prdjango 203.0.113.23

We will need to edit our settings file. Change ALLOWED_HOSTS, and replace the IP address with your domain name. Then redeploy your app using the git push dokku master command. At last, go to your DNS provider (probably where you bought your domain) and point an A record from yourdomain.com and www.yourdomain.com to your IP address. When you check your domain in your web browser, you should have a fully functional website live.

But… it’s not secured yet. Let’s fix that with Let’s Encrypt (it’s free).

sudo dokku letsencrypt prdjango
dokku letsencrypt:cron-job --add

Let’s Encrypt will expire every 3 months. The cron job will make sure that it gets renewed before it expires, so your site stays secure.

Some final notes:

  • It’s best practice to use a separate settings file in your Django project for local development. You can run that by adding --settings=local-settings.py. That way you don’t have to change anything in your settings files to keep both the local and live version running. If you don’t have many settings, you can also use environment variables (see point 2).
  • Use os.environ.get("DATABASE_URL", "") to get the database URL to be used in your settings file. It’s not recommended to hardcode such a URL in your settings.
  • Dokku is great for small scale projects. Dokku is not good for larger projects. It’s much harder to scale and doesn’t have an option to use high-availability for example.
  • Also don’t forget to set up a firewall and only allow port 443 and perhaps port 80 and 25. Installing Fail2ban is also a good idea to stop bots from trying to breach through SSH.