naughty cat logo

Deploy Dokku on a Digital Ocean Droplet, and Share Your App with the World

brenden gerber
Brenden GerberApr 7, 202418 min read
Deploy Dokku on a Digital Ocean Droplet, and Share Your App with the World

An in depth technical guide which takes you step by step through the entire process of setting up Dokku on a Digital Ocean Droplet.

Disclaimer: links in this article are affiliate links. Think of them like a small tip jar that helps keep the lights on during the creation of content like this.

 

In this step by step tutorial we’re going to learn how to set up Dokku on a Digital Ocean droplet so that we can start deploying our apps and websites quickly and affordably. This guide will cover everything from initializing your Digital Ocean droplet, to creating your first Dokku app, and everything in between.

 

If you’re still on the fence or aren’t sure of all the benefits of this setup, checkout my other article here which takes an in depth look at some of the cool features of both Dokku and Digital Ocean.

 

Prerequisites

The only thing you need to follow this tutorial is a Github repo with an app that you’re ready to share with the world!

 


 

1. Creating your droplet

First things first you’ll need a Digital Ocean account, you can create one here. Next go ahead and click Create -> Droplets. Now we’ll choose the settings for our new droplet.

 

Region

You’ll first need to select a region. Try to select one that will be near where most of your users are to cut down on latency.

 

Operating System

Here, if the technology your using calls for a specific OS, you’ll want to choose that (make sure the version matches too). If not, the selection is personal preference. I generally go with the latest version of Ubuntu.

 

Droplet Size

One nice thing about these VPSs is you can scale the RAM and processor up or down depending on your needs at any time. One limitation however, is that you can only scale the storage size up, and you can never scale it down.

 

For this reason I’ve chosen the $6 option because I have found 25GB to be sufficient for most of my uses, and this will allow up to scale the power of the droplet up or down freely at any time without being locked into a price tier because the size of the storage cannot be decreased.

 

Authentication Method

Select “SSH Key”, it’s a little trickier to get set up, but it’s far more secure and you’ll need to be comfortable working with SSH keys later on anyway. If you’re not familiar with SSH keys, they are basically long strings of characters stored on your computer. They are composed of a public/private pair that are used to authenticate you so you can gain access to your remote server from your computer. Follow the prompts on the screen to create a new SSH key if necessary and add the public key to your droplet.

 

Note: Do not create a passphrase for this key, we’ll be using it with Github actions later, and it’s drastically easier to do so without one.

 

Improved Metrics

Though not necessary, I recommend checking the box for improved metrics. This will give you access to a number of detailed graphs so you can track the usage of your droplet more in depth, which in turn will make managing your servers far easier.

 

Finishing Up

You can optionally add a host name, project, and any tags you want to your droplet. The rest of the settings can be left as is. Click Create Droplet, and give it a minute to spin up your shiny new VPS!

 


 

2. Setting Up Ubuntu for Production

There are a few steps to make sure our droplet is properly set up for production and following some basic best practices.

 

Create a User

Since the root user is a very powerful profile and has access to a lot of destructive actions that can mess things up for you very quickly (even by accident). For this reason we will create another user which we will use for normal every day tasks as a preventative measure.

 

1. Open your command line of choice and log in to your new droplet as the root user. This is where the magic of SSH shines as you don’t need to remember or enter a password when accessing the server from your main terminal.

 

ssh root@<your-droplet-ip>

 

2. Add a new user and choose a password. If you use the same username as your local machine you can connect automatically later without the need to specify a user which is nice. On Windows you can find this by navigating to C/Users where you will see a directory consisting of five letters, that is your username. Now just follow the prompts to complete the process.

 

adduser <your-username>

 

3. Set our user to have superuser (root) privileges. You’ll be able to access these privileges by typing the keyword “sudo” before a command that requires root access. This allows us to have the power of root from time to time, without the worry of accidentally doing something unintended. When using the keyword “sudo” be very conscious about what you’re doing.

 

If you’re like me, you’ve seen this everywhere, but maybe have no idea what it is or what it does. It originally stood for “super user do” in Linux, now it stands for substitute user do as it can be used to do commands from the perspective of other users too (not just root).

 

usermod -aG sudo <your-username>

 

4. Enable remote login for our new user so that in the future we can log in directly without having to log into root first and then switch. Running this command will copy the root’s SSH directory to the new user’s, preserve permissions, and update file owners. This will allow you to login as your new user using the same SSH details that you have already set up.

 

rsync --archive --chown=<your-username>:<your-username> ~/.ssh /home/<your-username>

 

Configure the Firewall

Security should always be on our minds, especially if we’re launching a production ready app. One of the first steps is a properly configured firewall to limit access to our droplet. For now, we only accept connections via SSH. If you start using more services later, you may need to adjust the firewall.

 

1. Allow OpenSSH so that we can connect to our server without interference from the firewall.

 

sudo ufw allow OpenSSH

 

2. Nginx is the web server that we'll use to route requests to our apps, so to continue, we'll need to allow it on our firewall.

 

sudo ufw allow 'Nginx http'
sudo ufw allow 'Nginx https'
sudo ufw allow 'Nginx full'

 

3. Enable the firewall.

 

sudo ufw enable

 

4. If you ever want to check what is currently allowed through the firewall.

 

sudo ufw status

 

5. If you ever want to view a full list of installed profiles which you may or may not want to allow.

 

sudo ufw app list

 

Choose Time Zone

For convenience it's nice to set our time zone as well. That way if we ever want to do things like run cron jobs at night when traffic is less, there's no math to figure out the timing.

 

1. Find our time zone in the list of available options.

 

timedatectl list-timezones

 

2. Set our time zone.

 

sudo timedatectl set-timezone <your-timezone>

 

Log In

Our new VPS is now spun up and configured for production. We can log in using our newly created user.

 

ssh <user>@<your-droplet-id>

 

Or if you set your droplet's username to be the same as your computer's.

 

ssh <your-droplet-id>

 

At this point it's a good idea to check that we have successfully set up a new user that can login with SSH and has sudo privileges.

 

sudo -l

 

After entering your sudo password, this will list your privileges, and if all is as it should be it will list "ALL".

 


 

3. Deploying Dokku

Now that our droplet is set up for production, it's time to deploy Dokku and configure some basic settings.

 

Install

If you are not already, log into your droplet remotely using the command line and then install Dokku. Feel free to go get a cup of coffee now, because this is going to take a few minutes.

 

wget -NP . https://dokku.com/install/v0.32.1/bootstrap.sh
sudo DOKKU_TAG=v0.32.1 bash bootstrap.sh

 

Copy SSH Keys

Log into your droplet as root (I know, I said never do this, but this is the one time only). We will now copy the SSH keys over to add them to Dokku. This is required for when it's time to push our app's repo to Dokku. Once your done, switch back over to your normal user account.

 

cat ~/.ssh/authorized_keys | dokku ssh-keys:add admin

 

Add a Global Domain

I like to set my global domain to my droplet's ip because I am going to use it to host several sites/apps and I want the default to be something neutral. If you are only going to be hosting one app, feel free to make the default your actual domain.

 

dokku domains:set-global <your-domain>

 

Create a Cron Job to Remove Old Containers

Since Dokku uses a new Docker container each time we build an app, if we're doing a lot of updates, they can pile up and eat through our droplet's storage before we know what's happened and our builds will start failing. Because of this we'll want to regularly clear out some of the old and unused containers. To do so we'll create a cron job, which will run a bash command to delete them.


1. Access our droplet's crontab. We'll use sudo here so we don't have to pass passwords to the script in order to run.

 

sudo crontab -e

 

2. Select your editor of choice, I go with 1 (nano) by default. Navigate to the end of the file and add a cron job that runs hourly.

 

0 * * * * yes | docker system prune -a

 

3. Now hit "ctrl + x" to exit, "y" to save, and "enter" to finish.

 


 

4. Creating a Dokku App

Now with our droplet set up and Dokku deployed, it's time to create our first app in Dokku which we will link to Github using Github actions for automatic deploys.

 

Navigate Dokku's CLI

First I want to give a quick overview of Dokku's CLI, I was pretty intimidated at first, as this was one of the first CLI tools I ever used, but don't worry, it's actually pretty intuitive and there are a plethora of help commands if we get confused.


The basic syntax for Dokku doesn't change much.

 

dokku category:command arguments

 

If you ever get lost or forget a command (I know I sure did) you can always access the help menu. It may seem overwhelming, but there aren't that many, and the descriptions are very good/concise. There's a ton of cool functionality that won't be covered in this tutorial too, so don't be afraid to explore a little!


Access a list of categories and their descriptions.

 

dokku help

 

Access a list of commands and their descriptions.

 

dokku category:help

 

Add an App

Choose a descriptive name for your app. I often include the project name using hyphens in place of spaces, followed by "app". This will make it easy to distinguish from any other services like databases used in our project.

 

dokku apps:create <your-app-name>

 

Connect a Domain

Now we can connect a domain to our app so anyone anywhere can access it! If you don't have one yet, I always recommend Namecheap because they're affordable, have an awesome UI, and are an industry standard. You can also choose to skip this step and access your app via your container's ip for the time being.

 

1. To do so, we'll need to set up our domain's DNS. I like to point to Digital Ocean for our DNS, so all of our droplet's settings are in the same place. Every provider is different, but the gist is to add three custom DNS servers to our domain. If you're having trouble, here's a list of instructions for popular providers from Digital Ocean's documentation.

 

ns1.digitalocean.com
ns2.digitalocean.com
ns3.digitalocean.com

 

2. Once we have that set up we can go back to our Digital Ocean account and navigate to Networking -> Domains. Now we can add our domain and configure it. To do so we will need to add a couple of records.

 

Type          Hostname      Value
A             @             <your-droplet-ip>
cname         www           <your-domain.com>

 

If you rather host a backend, admin portal, etc on a subdomain, you can also set up all sub domains to point over to your droplet instead.

 

Type         Hostname                  Value
A            *.your-domain.com         <your-droplet-ip>

 

3. Now that all of our traffic is coming to our droplet, our Nginx server will decide where to route requests. Thankfully Dokku makes configuring Nginx to direct traffic an absolute breeze.


We can first add the URL to our Dokku app.

 

dokku domains:add <your-app-name> <your-url.com>

 

Then we will want to forward the ports to the port that our app is set to listen on inside the container where it's deployed.

 

dokku proxy:set <your-app-name> nginx
dokku ports:add <your-app-name> https:443:<your-port>
dokku ports:add <your-app-name> http:80:<your-port>

 

Add SSL

Now let's set up our SSL because we definitely want our new app to be secure. Thankfully this is another task that Dokku makes incredibly easy by integrating with Let's Encrypt.


Note: Be aware that there is a rate limit with Let's Encrypt that's not too hard to reach if you're just playing around/practicing with Dokku.

 

1. Download the plugin. 

 

sudo dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git

 

2. Set the email where emails regarding any issues that might arise with our certificates will be sent. We only have to do this once, not for each individual app.

 

dokku letsencrypt:set - global email <your-email>

 

3. Enable the Let's Encrypt Integration.

 

dokku letsencrypt:enable <your-app-name>

 

Now hopefully this will display a cool success message, but if you’re like me you may have forgotten to allow this traffic in your firewall, if so don’t spend hours slamming your head against your desk, just go back to “Configure the Firewall” and make sure you are allowing traffic through on http and https.

 

4. One of the big hassles with managing your own ssl certificates has always been renewal. Again Dokku comes to our rescue, and we can easily set a cron job to automatically renew the certificate before it expires so we don’t have to do so manually, hurray!

 

dokku letsencrypt:cron-job --add

 

Environment Variables

We'll likely need to add a number of environment variables so our code works as intended. You can enter these one at a time or you can add multiples by simply adding more key/value pairs separated by a space. Dokku will rebuild our app automatically after updating any environment variables.

 

dokku config:set <your-app-name> <YOUR_ENV_VAR>=<yourEnvVarValue>

 

Push Code

At this point you might be thinking okay great, but what about my code. In this last step we’re going to finally set up Github actions to automatically deploy when we push to origin of our main (or any specified) branch.

 

1. Head over to the Github repo that you want to push live and navigate to Settings -> Secrets and Variables -> Actions -> New Repository Secret. There are three secrets that we will need to add which will be used by our deploy script (remember these are things we definitely don’t want to commit to source control).

 

APP_NAME    (The name of your app as it is in Dokku)
HOST        (The ip of the droplet where you have Dokku deployed)
SECRET      (Your private SSH key)

 

2. In your project create a file “deployment.yml” in “.github/workflows/”. This is what Github will use each time you push changes to the main branch of origin to deploy your app to Dokku. It will likely take a few minutes for the action to complete and for the build to succeed.

 

Note: If you have a large app it’s possible that your build will fail due to running out of memory. In case of errors the first thing you should do is check out the graphs for your droplet and see if the ram is maxing out, if so simply increase the size of the droplet for the build process (we can decrease it again after to keep our monthly costs down).

 

---
#Configures github actions to deploy on push to origin's main branch

name: 'deploy'

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Cloning repo
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Push to dokku
        uses: dokku/github-action@master
        with:
          git_remote_url: "ssh://dokku@${{ secrets.HOST }}:22/${{ secrets.APP_NAME }}"
          ssh_private_key: ${{ secrets.SECRET }}

 

Add a Database

As an optional step your app may require a database. Again Dokku's magic makes this incredibly easy.


1. Install the database plugin. I will be showing how to set up a PostgresSQL server, but Dokku supports a number of other databases as well. You can find more information on those here.

 

sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git

 

2. Spin up the database server.

 

dokku postgres:create <your-db>

 

3. Link the database to your app. This will create an environment variable DATABASE_URL which you can use in your app to connect to the database. It will also print out the URL to the console for you to make note of. 

 

dokku postgres:link <your-db> <your-app-name>

 

The connection URL will always be in the same format.

 

postgres://DATABASE_USERNAME:DATABASE_PASSWORD@DBHOST:DATABASE_PORT/DATABASE_NAME

 

4. Run some initial queries. We may need to set the database up to be used by our app, and it's usually easier to do so by connecting remotely using pgAdmin or a similar tool. To do so we will first need to expose the database to be accessed outside of the container.

 

dokku postgres:expose <your-database> <some-port>

 

Now using the information in DATABASE_URL we can enter then connection information and run any queries we need. Just make sure to use the port we exposed above instead of the port listed in the connection URL.

 

Backup the Database

Knowing our data is safe and secure has never been more important. Again, Dokku makes this a simple process, with a number of automated backup solutions. We will be learning how to backup our database to an AWS S3 bucket because it is a fantastic and very affordable service. You can find information on other solutions here.

 

1. Create an AWS account. Then navigate to Services -> s3 -> Create a Bucket (you may need to search for s3 as there are a lot of services). Most of the settings can be left as is, there are only three we need to change.

 

2. Set bucket to delete backups after 30 days. This is an optional step to keep costs down. Navigate over to Services -> s3 -> Your-Bucket -> Management -> Create Lifecycle Rule.

 

3. Create a user to give an access key to. Navigate to Services -> IAM -> Access Management -> Users -> Create User (again, you may need to search for IAM as there are a lot of services). Once you have created the user navigate to Users -> Your-User -> Permissions -> Add permission -> AmazonS3FullAccess.


4. Create the access key. Navigate to Users -> Your-User -> Security Credentials-> Create Access Key -> Other. Once you have generated your key pair, take note of the secret key as this will not be available again.


5. Setup automatic updates for our database to run every night at midnight. Head back to your droplet's command line for this step.

 

dokku postgres:backup-auth your-db your-aws-access-key-id your-aws-secret-access-key
dokku postgres:backup-schedule your-db "0 0 * * *" your-aws-bucket

 


 

Phew

That was a lot to cover, but hopefully if you made it this far you have an app up and running and accessible to the world! Now pat yourself on the back for a job well done, and go do something to relax a bit, you've earned it!

 

And as always, if you ever get stuck feel free to head over to Naughty Cat and shoot us a message; we're always happy to chat or help troubleshoot. We're also always available for consulting, web development, maintenance, hosting, and anything else web related!

Tags:
#Hosting
#HeadlessCMS
#DigitalOcean
#Dokku
#Technical
#Tutorial
#OpenSource