# Deploying the Backend on Dokku

This guide describes how to deploy the platform backend on Dokku (opens new window). It covers only the backend; to deploy the complete platform (frontend, backend and database) on one server, we recommend the Docker Compose approach described in Production Deployment.

All names below are placeholders; replace them with your own:

Placeholder Meaning
my-server Dokku app name
my_postgres PostgreSQL service name
my_redis Redis service name
api.example.com Backend domain
dokku.example.com Dokku host

WARNING

The steps on this page are compiled from the platform source code and the official Dokku documentation, and have not yet been verified end to end on a Dokku host. If you run into problems, contact us through the community (opens new window).

# Deployment Steps

# 1. Create the App

ssh [email protected]
dokku apps:create my-server
1
2

# 2. Add Persistent Storage

Attachments and application seed data must be kept in host directories so they survive container rebuilds:

mkdir -p /var/lib/dokku/data/storage/my-server/attachments
mkdir -p /var/lib/dokku/data/storage/my-server/data/csv
dokku storage:mount my-server /var/lib/dokku/data/storage/my-server/attachments:/app/attachments
dokku storage:mount my-server /var/lib/dokku/data/storage/my-server/data:/app/plugin/data
1
2
3
4

The data directory has the same structure as codes/data in the platform repository (subdirectories such as csv, groovy and attachments), so you can copy the contents of codes/data into it directly.

# 3. Add PostgreSQL and Redis

# Install the plugins the first time
dokku plugin:install https://github.com/dokku/dokku-postgres.git postgres
dokku plugin:install https://github.com/dokku/dokku-redis.git redis

# Create PostgreSQL (with the same pgvector image as Docker Compose) and link it to the app
dokku postgres:create my_postgres --image pgvector/pgvector --image-version 0.8.0-pg17
dokku postgres:link my_postgres my-server

# Create Redis and link it to the app
dokku redis:create my_redis
dokku redis:link my_redis my-server
1
2
3
4
5
6
7
8
9
10
11

The backend can use the REDIS_URL injected by redis:link directly.

postgres:link injects DATABASE_URL (in postgres://... format), but the backend reads JDBC_DATABASE_URL, JDBC_DATABASE_USERNAME and JDBC_DATABASE_PASSWORD, which you set in the next step.

# Importing the Initial Database

This differs from the Docker Compose approach: when the database starts for the first time, Docker Compose automatically runs the scripts under db-seed-data/initdb in the platform repository, which restore the initial database seed-data.dump and create the vector extension. Dokku creates an empty database, and these scripts are not run.

To match the initial state of Docker Compose, before deploying the backend for the first time, copy db-seed-data/initdb/seed-data.dump from the platform repository to the Dokku host and import it:

# Run on your local machine to copy the initial database to the Dokku host
scp db-seed-data/initdb/seed-data.dump [email protected]:/tmp/

# Run on the Dokku host
dokku postgres:import my_postgres < /tmp/seed-data.dump
1
2
3
4
5

postgres:import imports custom-format dumps with pg_restore. seed-data.dump contains CREATE EXTENSION vector, which is why the database above must be created with the pgvector image. The current backend version does not use vector features, so the vector extension itself is not required.

# 4. Set Environment Variables

First, look up the database connection information:

dokku postgres:info my_postgres --dsn
# The output looks like postgres://postgres:<password>@dokku-postgres-my_postgres:5432/my_postgres
1
2

Set the JDBC connection and other variables based on the output:

dokku config:set --no-restart my-server \
  JDBC_DATABASE_URL=jdbc:postgresql://dokku-postgres-my_postgres:5432/my_postgres \
  JDBC_DATABASE_USERNAME=postgres \
  JDBC_DATABASE_PASSWORD=<password> \
  JWT_SECRET=$(openssl rand -hex 32) \
  TENANT_ID=muyan \
  SEED_DATA_FOLDER=/app/plugin/data \
  GRAILS_ENV=development \
  PORT=8080
1
2
3
4
5
6
7
8
9
Variable Description
JDBC_DATABASE_URL, etc. Database connection. When not set, the backend connects to localhost:5432/platform and fails to start on Dokku
JWT_SECRET Signing key for login tokens, at least 32 bytes. When not set, the public default value is used and anyone can forge tokens
TENANT_ID Tenant name. Must be set, and must match the tenant (muyan) in the imported seed-data.dump
SEED_DATA_FOLDER Application seed data directory; must match the mount path from step 2
GRAILS_ENV Keep it as development, which refreshes platform model definitions and seed data on every startup, consistent with the Docker Compose configuration
PORT Port the backend listens on; the startup script passes it to the backend as -Dserver.port=$PORT

TIP

You can also set PAAS_VENDOR=heroku so that the image's startup script converts DATABASE_URL into JDBC_DATABASE_* automatically (the startup script performs this conversion only for heroku, fly and railway). PAAS_VENDOR=dokku does not trigger the conversion.

The backend log contains secrets

In the current version, the backend image runs printenv at startup, writing all environment variables (including JDBC_DATABASE_PASSWORD and JWT_SECRET) to the log, where dokku logs my-server shows them directly. This is an issue in the current version. Do not paste logs as-is into public channels or support tickets, and restrict the accounts that can log in to the Dokku host and run dokku logs. If you suspect the logs have leaked, replace the database password and JWT_SECRET.

# 5. Deploy the Backend Image

Pulling the backend image muyantech/backend requires logging in to Docker Hub. The token is token.txt in the platform repository. Copy it to the Dokku host first (for example scp token.txt [email protected]:/root/), then run on the Dokku host:

cat /root/token.txt | dokku registry:login --global --password-stdin docker.io muyantech
dokku git:from-image my-server muyantech/backend:1.0.0-beta18
1
2

The image declares three ports, 8080, 10000 and 5005, and by default Dokku exposes all of them. Keep only 8080:

dokku ports:set my-server http:80:8080
1

# 6. Bind a Domain and Enable HTTPS

dokku domains:add my-server api.example.com

dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git
dokku letsencrypt:set my-server email [email protected]
dokku letsencrypt:enable my-server
1
2
3
4
5

After enabling HTTPS, run dokku ports:report my-server again and confirm that only the http/https mappings for 8080 remain.

# 7. Upgrade

dokku git:from-image my-server muyantech/backend:<new-version>
1

Before upgrading, read the Upgrade Notes, and back up the database first with dokku postgres:export my_postgres > backup.dump.

# Attachment Storage Configuration

By default, attachments are stored according to the following two system configurations (DynamicConfig); check their values:

  1. attachment.storageEngine (attachment storage engine) is LOCAL_FILE
  2. attachment.local.folder (storage directory of the local engine) is /app/attachments, matching the mount path from step 2

Otherwise attachment uploads may fail, or uploaded attachments may be lost when the container is rebuilt. You can also set the environment variable ATTACHMENT_LOCAL_FOLDER=/app/attachments directly; it takes precedence over the system configuration.

# Dokku Command Reference

# Basic App Maintenance

  • Create an app: dokku apps:create <app-name>
  • Delete an app: dokku apps:destroy <app-name>
  • List all apps: dokku apps:list
  • View app logs: dokku logs <app-name>
  • Manage environment variables:
    • Show all environment variables of an app: dokku config:show <app-name>
    • Set environment variables for an app: dokku config:set <app-name> KEY1=VALUE1 KEY2=VALUE2
    • Remove environment variables from an app: dokku config:unset <app-name> KEY1 KEY2
  • Stop and start an app:
    • Stop the app: dokku ps:stop <app-name>
    • Start the app: dokku ps:start <app-name>
    • Restart the app: dokku ps:restart <app-name>
  • Mount storage:
    • Create a mount: dokku storage:mount <app-name> <host-dir>:<container-dir>
    • List mounts: dokku storage:list <app-name>
    • Apply mounts: dokku ps:restart <app-name>
    • Remove a mount: dokku storage:unmount <app-name> <host-dir>:<container-dir>
  • Manage domains:
    • Add a domain: dokku domains:add <app-name> your-subdomain.yourdomain.com
    • Show domains: dokku domains:report <app-name>
    • Remove a domain: dokku domains:remove <app-name> your-subdomain.yourdomain.com
  • Help: add --help to any command to see its usage.

# Redis Plugin

  • Create a service: dokku redis:create <service-name>
  • Link to an app: dokku redis:link <service-name> <app-name>
  • Delete a service: dokku redis:destroy <service-name>
  • Show service info: dokku redis:info <service-name>

# PostgreSQL Plugin

  • Create a service: dokku postgres:create <service-name>
  • Link to an app: dokku postgres:link <service-name> <app-name>
  • Delete a service: dokku postgres:destroy <service-name>
  • Show service info: dokku postgres:info <service-name>
  • Export a backup: dokku postgres:export <service-name> > backup.dump

# Let's Encrypt Plugin

  • Set the notification email: dokku letsencrypt:set <app-name> email [email protected]
  • Enable Let's Encrypt: dokku letsencrypt:enable <app-name>
  • Show certificate info: dokku letsencrypt:list
  • Automatic renewal: dokku letsencrypt:cron-job --add
Last Updated: 9/24/2026, 2:27:35 PM