Background Link to heading

I recently setup dokku to host a handful of self-hosted and personal apps. Fast-forward a few months and I can’t remember how to deploy a new app with my setup 😂

For future-self, I’m going to document here how to deploy mealie, which at the time of writing suggests the following docker compose file. This can be adapted for anything that can be deployed with a single Dockefile.

services:
  mealie:
    image: ghcr.io/mealie-recipes/mealie:v2.4.1
    container_name: mealie
    restart: always
    ports:
        - "9925:9000" 
    deploy:
      resources:
        limits:
          memory: 1000M
    volumes:
      - mealie-data:/app/data/
    environment:
      # Set Backend ENV Variables Here
      ALLOW_SIGNUP: "false"
      PUID: 1000
      PGID: 1000
      TZ: America/Anchorage
      BASE_URL: https://mealie.yourdomain.com

volumes:
  mealie-data:

Prep dokku Link to heading

Create a new new app in dokku:

dokku apps:create mealie

Now set some env vars:

dokku config:set mealie BASE_URL=https://mealie.yourdomain.com
dokku config:set mealie TZ=America/Anchorage
dokku config:set mealie ALLOW_SIGNUP=false

Next, match the compose file and set a 1gb memory limit:

dokku resource:limit --memory 1000 mealie

Now we’ll map some persistent storage, this requires a few commands:

dokku storage:ensure-directory mealie

Note the actual directory on the host in the output of this command, for me, it is /var/lib/dokku/data/storage/mealie. Now mount that directory into the app:

dokku storage:mount mealie /var/lib/dokku/data/storage/mealie:/app/data 

Since this is a bind mount on the host, there may be permission issues, depending on the container. The storage:ensure-directory command has multiple options for setting the uid/gid if you run into issues. Certain containers also support uid/gid mapping (like this one with the PUID, PGUID env vars), so use those if needed.

Finally, double check the domain (it should be automatically, matching the app name).

dokku domains:report

If for some reason you have varying dokku app name and target sub-domains, you can manage it via the dokku domains:[add|remove] sub-commands, just make sure dokku domains:report looks right before proceeding.

Create Dockerfile for the app Link to heading

We’ll use a Dockerfile to trigger the dokku deploy. There are multiple approaches to deploy apps in dokku, but I find this one clean and easy to manage.

FROM ghcr.io/mealie-recipes/mealie:v2.4.1
EXPOSE 9000

Create a new directory, put that Dockefile in it, and init a new git repo:

mkdir dokku-mealie
cd dokku-mealie
# create Dockerfile referenced above
git init
git add .
git commit -m "Initial commit"

At this point, I normally push the repo to a git remote. Prefixing these repos with dokku- makes is easy to identify and manage repos that hold dokku apps.

Now we’re ready to push to dokku:

git remote add dokku dokku:mealie
git push dokku master

Now just 🙏 it works.

Note: I setup a host entry in my ssh config (~/.ssh/config) to simplify adding dokku origins:

Host dokku
        HostName my-hostname-or-ip
        User dokku
        IdentityFile ~/.ssh/my-ssh-key

Expose the app Link to heading

At this point, the app is probably exposed on http port 9000. We don’t want that, instead let’s make sure it’s exposed over https.

I am using cloudflare’s dns proxy to handle my traffic and their built in certificate management. So that means I don’t need lets-encrypt to issue me cerifificates within dokku, but I do need to install the cloudflare certificate on a per-app basis (even though it is a wildcard cert, I can’t figure out how to install a global cert in dokku):

dokku certs:add mealie < cert-key.tar

Where cert-key.tar is an archive consisting of two files, server.crt and server.key (via cloudflare).

Next let’s let dokku know it should server this over https:

dokku ports:add mealie https:443:9000
dokku ports:remove mealie http:9000:9000

At this point, everything should work :)