dokku-postgres turns PostgreSQL into a dokku service with a link command
a postgres plugin for dokku
At a glance
- What is it?
- dokku-postgres is the official PostgreSQL plugin for dokku, running the database as a container alongside your apps and wiring it to them with DATABASE_URL. It ships around forty commands for the service lifecycle, S3 backups and major version upgrades, and it requires dokku 0.35.x or newer.
- Who is it for?
- dokku-postgres fits a team already running apps on dokku that wants a database managed by the same tool and injected as DATABASE_URL, and it does not fit a team that needs a managed PostgreSQL service with its own failover, backups and monitoring.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A database container that dokku manages like any other service
dokku is a small PaaS that runs applications as containers on a single host, and this repository is the official PostgreSQL plugin for it. The plugin's job is to make Postgres look like one of the platform's services: something you create, link to an application, back up, upgrade and eventually destroy through a consistent set of commands.
The README states that the plugin currently defaults to installing postgres 18.6, which is the official Docker image for that version. The requirements are short: dokku 0.35.x or newer, and docker 1.8.x. That dokku version floor is worth noticing, because it means the plugin is tied to a recent platform release, and a host on an older dokku cannot simply add it.
The audience is a developer or small operations team running their own applications on their own machine, where a full managed database service is either unavailable or out of budget. This is infrastructure as a set of commands. There is no dashboard, no configuration file to edit, and no separate agent. The whole surface is the command list.
The plugin is MIT licensed and written primarily in shell, which is the native implementation language for dokku plugins and the reason it can hook into the platform's own service model rather than sitting beside it.
Installation is a single dokku plugin:install call
Installing the plugin means telling dokku where to get it, and naming the command prefix it will own:
# on 0.35.x+
sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git --name postgresThe `--name postgres` argument is the part with lasting consequences. It fixes the command prefix that every operation in this article uses, and it is what lets a host run several datastore plugins side by side without their commands colliding. The prefix is not hard coded in the shell scripts, either, since the Makefile defines it as a variable and derives the rest of the build from it.
The install step is also where the version of the database is decided indirectly, because the plugin resolves the image and version itself unless you override them at create time. So the sequence for a first deployment is: install the plugin, create a service, link it to an app, and then set the schema up inside the database. The README's basic usage section documents the create command with its usage line:
# usage
dokku postgres:create <service> [--create-flags...]Note that the service is named by the operator. A host with a staging and a production database names them differently, and every later command takes that name as its first argument.
Forty commands, all sharing one prefix and one argument shape
The command list is long, and reading it tells you the whole feature set. The lifecycle commands are `postgres:create`, `postgres:destroy`, `postgres:start`, `postgres:stop`, `postgres:restart`, `postgres:pause`, `postgres:info`, `postgres:list` and `postgres:exists`. Data operations include `postgres:export`, `postgres:import`, `postgres:clone`, `postgres:reset` and `postgres:connect`, and `postgres:enter` runs a command inside the running container.
The linking commands are the heart of the integration. `postgres:link` links the service to an app, `postgres:unlink` removes it, `postgres:linked` and `postgres:links` query the current state, and `postgres:promote` sets the service as `DATABASE_URL` in an app. That last one is the mechanism most applications care about, since an app configured to read `DATABASE_URL` needs no change at all when the database behind it is replaced.
Networking is handled by `postgres:expose`, `postgres:unexpose` and `postgres:reexpose`, with the expose command taking custom host and port pairs, or assigning a random port on the 0.0.0.0 interface if no port is given. There is also `postgres:certificate`, which prints the certificate the service encrypts connections with, and `postgres:mount` and `postgres:unmount` for attaching host paths or Docker volumes into the container.
The help story is documented: help for any command comes from passing the command to `postgres:help`, and the plugin's documentation is generated from the help output together with the files in the `docs/` folder. Undocumented commands are still reachable through that help output, which is the one place to look for behaviour the README does not cover.
Create flags configure the container, not just the database
The flags accepted by `postgres:create` are where the plugin stops being a thin wrapper around the official image. The README lists them, and they fall into three groups.
The first group changes what runs. `--definition` selects the definition to run the service on instead of the one its image and version resolve to, `-i|--image` and `-I|--image-version` override the image name and version, and `-N|--initial-network` and `-P|--post-create-network` control which Docker networks the container joins, before and after creation. The second group adjusts the container itself: `--log-driver` and `--log-opt` set the Docker logging driver and its key=value options, and `-m|--memory` sets a memory limit in megabytes, which is unlimited by default.
The third group is about the service semantics. `-c|--config-options` passes extra arguments to the process the container runs rather than to Docker, and the flag description makes a point of it: use mount for mounts. `-C|--custom-env` takes semi-colon delimited environment variables, and `-p|--password` overrides the user level service password for datastores that have one.
The `-p` flag deserves a moment. A user level password is one that the platform knows so it can reconnect for administrative tasks, and overriding it changes who can reach the database. The same caution applies to `-i|--image`: pointing the service at a different image is supported, and it is also the fastest way to end up running something that is not PostgreSQL under a command set that assumes it is.
Backups are scheduled to S3 and need credentials set separately
The backup subsystem is the most involved part of the plugin, and it is built around an existing S3 bucket. `postgres:backup` creates a backup of the service to a bucket, `postgres:backup-schedule` schedules one with a schedule expression, `postgres:backup-unschedule` removes the schedule, and `postgres:backup-schedule-cat` prints the crontab line that was installed. `postgres:backup-logs` prints the most recent output of the scheduled backups.
Authentication is its own set of commands. `postgres:backup-auth` takes the AWS access key id, the secret access key, the default region, the signature version and an endpoint URL, and `postgres:backup-deauth` removes them again. A separate `-u|--use-iam` flag appears on both `postgres:backup` and `postgres:backup-schedule`, which is the path for instances where the host has an IAM role rather than static keys.
Encryption is configured on the same service. `postgres:backup-set-encryption` sets a passphrase for all future backups and `postgres:backup-unset-encryption` removes it, while `postgres:backup-set-public-key-encryption` and `postgres:backup-unset-public-key-encryption` do the same with a GPG public key id.
The practical reading is that a backup is only as good as the credentials and the bucket, and neither is verified by the plugin. A scheduled job that fails silently looks exactly like a successful one until you restore it, which is why running `postgres:backup` once by hand and then reading `postgres:backup-logs` is the sensible first step.
Cloning and upgrading across major versions have their own cleanup step
Three commands cover the operations that are hard to do by hand.
`postgres:clone` creates a new container and copies the data from one service into the new one, which is how you make a copy of a production database for testing without hand wiring a replication setup. `postgres:export` writes a dump of the service database to a file, and `postgres:import` reads one back in. Both accept `--all-databases` for a dump that covers more than the default database, and both pass extra arguments through to the underlying export and import tools.
Upgrading is a two step affair. `postgres:upgrade` upgrades a service to the specified versions, and `postgres:upgrade-cleanup` removes the data that an upgrade across a major version kept aside. That second command only exists because a major version upgrade in PostgreSQL cannot simply overwrite the data directory, so the plugin preserves the old data instead of deleting it, and the operator decides when to discard it.
Two related commands deserve a mention. `postgres:reset` deletes all data in the service while keeping the service and its links, which is the fast way to get a clean database with the same configuration and the same app connections. `postgres:destroy` deletes the service, its data and its container, and its description says it will only do so if there are no links left, so the guard against destroying a database another app depends on is built in.
The README is generated by the same binary the plugin runs
The build tooling explains something the README does not advertise. The Makefile notes that the readme is generated by the same binary the plugin runs, which is already installed on a host running dokku and is fetched into `tmp` everywhere else. The path used is `/var/lib/dokku/data/postgres/dokku-datastore`, and the plugin falls back to `tmp/dokku-datastore` when the installed copy is absent.
That binary is `dokku-datastore`, and the Makefile pulls the datastore version out of the `config` file with a sed expression, which is how a plugin stays aligned with the version of the shared datastore layer the host has installed. The same file also resolves the architecture, mapping aarch64 and arm64 to arm64 and everything else to amd64, and pins the tool versions used for checks: shfmt 3.0.2, XUnit to GitHub 0.3.0 and XUnit reader 0.1.0.
The development toolchain is visible too. The `bats` target installs bats-core through brew on macOS and clones it from GitHub elsewhere, `shellcheck` does the same for shellcheck, and there is a `shfmt` target for formatting. The tests are therefore bats based, which is the standard for dokku plugins, and they run against a real host rather than a mock.
The repository layout follows the plugin convention: `commands/`, `config/`, `cron-entries/` for the scheduled backup entries, `datastore/`, `docs/`, `install/`, `plugin.toml`, the `pre-build`, `pre-delete`, `pre-start` and `pre-restore` lifecycle hooks, `post-app-clone-setup` and `post-app-rename-setup`, `service-list`, `subcommands/`, `tests/` and `update`. Every lifecycle event in the platform has a matching hook, which is the clearest sign of how deeply this plugin is wired into dokku rather than sitting on top of it.
Editorial conclusion
dokku-postgres fits a team already running apps on dokku that wants a database managed by the same tool and injected as DATABASE_URL, and it does not fit a team that needs a managed PostgreSQL service with its own failover, backups and monitoring. Before you rely on the backup commands, note that they write to an S3 bucket you supply and that credentials are set separately, so run `postgres:backup` and check `postgres:backup-logs` on a test service before you trust them with data you cannot recreate.
Frequently asked questions
How do I install the dokku postgres plugin?
You run `sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git --name postgres` on a host with dokku 0.35.x or newer and docker 1.8.x. The name you pass fixes the `postgres:` command prefix that all later operations use.
How does an app get the DATABASE_URL for a dokku postgres service?
You link the service with `postgres:link`, or set it as the app's DATABASE_URL with `postgres:promote`. The plugin also provides `postgres:linked` and `postgres:links` to inspect the current links, and `postgres:unlink` to remove one.
Where do dokku postgres backups go and how are they scheduled?
Backups are written to an S3 bucket you supply. `postgres:backup-schedule` sets the schedule, `postgres:backup-auth` sets the AWS credentials and region, the `-u|--use-iam` flag uses an IAM role instead of static keys, and `postgres:backup-logs` prints the recent output of the scheduled backups.
What happens to my data when I upgrade PostgreSQL through dokku?
A major version upgrade cannot simply overwrite the data directory, so the plugin keeps the old data aside rather than deleting it. `postgres:upgrade-cleanup` is the command that removes that data once you are satisfied with the upgrade.
Which PostgreSQL version does the dokku postgres plugin install?
The README states that the plugin currently defaults to installing postgres 18.6 from the official Docker image. The image and version can be overridden at create time with the `-i|--image` and `-I|--image-version` flags.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/dokku-dokku-postgres)Was this useful?