Background Link to heading

I recently set up 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 my 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 Dockerfile.

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 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, PGID env vars), so use those if needed.

Finally, double check the domain (it should be automatic, 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 Dockerfile 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 it 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 set up 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 Let’s Encrypt to issue me certificates 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 serve this over https:

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

At this point, everything should work :)