Skip to main content

portmgr

portmgr is a wrapper around docker-compose that allows running typical docker-compose commands recursively. Additionally, it shortens commands to a single letter.

Let's say you have organized your compose files like this, you just add a dckrsub.yml in each parent folder:

docker/
├── dckrsub.yml
├── reverse-proxy/
│   └── docker-compose.yml
├── storage
│   ├── dckrsub.yml
│   ├── nextcloud/
│   │   └── docker-compose.yml
│   └── immich/
│       └── docker-compose.yml
└── scripts

Each dckrsub.yml has a list of subdirectories, which portmgr should decend into. For example, the dckrsub.yml in docker/ might look like this:

- reverse-proxy
- storage

And the dckrsub.yml in docker/storage/ like this:

- nextcloud
- immich

Now, if you run portmgr u in docker/ it will run docker compose up -d in reverse-proxy/, storage/nextcloud/ and storage/immich/.

portmgr starts from the current directory, so when running it in docker/storage/, it will run docker compose only in nextcloud/ and immich/. You can also use it in a directory with a docker-compose.yml as a shortener for docker-compose commands.

Commands

The following commands are available. The respective docker-compose commands are in brackets.

  u   Create and start containers (up)
  p   Pull images (pull)
  s   Stop services (stop)
  d   Stop and remove containers (down)
  l   Show container logs (logs)
  a   Run shell in container (exec -it <service> sh)
  b   Build images (build)
  c   List containers (ps)
  t   List processes in containers (top)
  r   Build and push to registry (build, push)
  v   Scan container images for vulnerabilities
  E   Encrypt/seal secret file(s) with age (requires portmgr[secrets])
  D   Decrypt/unseal sealed secret files (requires portmgr[secrets])
  S   Show secret migration status (requires portmgr[secrets])
  M   Move literal environment values from the compose file into .env
  R   Rotate postgres/mariadb passwords and write new values to .env

You combine multiple commands. For example portmgr dul, runs docker compose with down, up and logs, thus stopping, removing and starting all containers and then showing the logs.

Installation

sudo pip install portmgr

Or build it from source (here using the latest commit on master branch)

sudo pip install https://github.com/Craeckie/portmgr.git

Sealing secrets

portmgr can encrypt secret-bearing files (.env, config files) with age so they are safe to commit to git. Decryption happens once at setup time; the plaintext files remain on disk for Docker to use normally.

Requires the secrets extra:

pip install portmgr[secrets]

Move secrets out of the compose file first (run inside a service directory, or above it to go through every stack):

portmgr M

Lists each literal environment: value with secret-looking keys (password, token, key, …) preselected, asks which to move, previews the result and asks for confirmation. Each chosen value is appended to .env (created with mode 600 if missing) and replaced in the compose file with "${NAME}"; nothing else in the compose file changes, comments included. NAME is the key itself, or <SERVICE>_<KEY> when that name is already used with another value, is referenced elsewhere in the file, or is set in your shell (which would override .env). Values that already reference a variable, span several lines, use YAML anchors or tags, or that compose would re-type (True, 0o17, 1.50, …; quote them to move them) are left alone with a note. Then seal .env as below and commit the compose file.

Seal a file (run inside a service directory):

portmgr E .env

This creates .env.age and writes a .migrated marker. Commit the .age file and .migrated; never commit the plaintext — E does not touch .gitignore, so make sure .env is ignored yourself.

Decrypt on a new server (run from any ancestor directory):

portmgr D

Recurses via dckrsub.yml and decrypts every *.age whose plaintext is missing. Pass --force to overwrite existing plaintext from the sealed copy.

Check migration progress:

portmgr S

Reports DONE, PENDING, or CLEAN for each service and prints a summary tally. A .migrated marker always means DONE, even if the compose file still contains what looks like a secret — create it by hand for services that need no sealing. CLEAN only means nothing was detected; portmgr u's footer still counts a CLEAN service as unsealed until it has a .migrated.

Rotate database passwords:

portmgr R

Finds postgres and mariadb/mysql services in each compose stack, generates a new random password, runs ALTER USER in the running container, and writes the new value(s) to .env. For mariadb/mysql it uses the mariadb client, or mysql where that is the only one (e.g. mysql/mysql-server), and changes the password of every host entry of the account (root@localhost and root@% alike). The value goes into .env under the variable the compose file reads: MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASS}" updates MYSQL_ROOT_PASS. A value that mixes a reference with other text (pre${X}) is not rotated. If a password was previously hardcoded in the compose file rather than referenced via ${VAR}, a warning is printed reminding you to update the compose file to use the variable reference.

Every rotated password is printed under its .env name, with the old and the new value each on its own old: / new: line, also when only part of a service could be rotated, so you can replace the old value wherever else it is configured. The output contains the passwords in clear text; keep it out of logs you share. Re-seal .env with portmgr E .env afterwards if you keep a .env.age.

Passphrase: portmgr prompts once and caches the passphrase for the rest of the run. For non-interactive use (e.g. in scripts or provisioning), set:

PORTMGR_PASSPHRASE=<passphrase> portmgr D

After running portmgr u, a footer line is printed if any services have not yet been sealed.

Tipps

If you use portmgr a lot like me, you might want to shorten it to one letter. For bash, you can add alias p='portmgr' to ~/.bashrc. For fish-shell you can add abbr p portmgr to ~/.config/fish/config.fish.

Release files for portmgr 1.11.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for portmgr 1.11.1
File Size Uploaded
portmgr-1.11.1.tar.gz 45.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for portmgr 1.11.1
File Interpreter ABI Platform
portmgr-1.11.1-py3-none-any.whl Python 3 none any Details

Total release size: 74.5 kB

Release files / portmgr-1.11.1.tar.gz

Download URL portmgr-1.11.1.tar.gz
Size 45.4 kB
Tags Source
SHA-256 checksum
How to use checksums
240b181cd16ec563f56e383b952ab9749d6c4e5722cfe7725f1d23b520d8f684
BLAKE2b-256 checksum
How to use checksums
8414e85129631cc217658fa0713bd0492ea8c294755aeba3d06b6245df74da1d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / portmgr-1.11.1-py3-none-any.whl

Download URL portmgr-1.11.1-py3-none-any.whl
Size 29.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b4134853ba58207e73412e3548e6222cabc2ed3bb91b68c30d15fb42e7c466fb
BLAKE2b-256 checksum
How to use checksums
3597f3c74454d74bfd14be55ae766f375309c0e5e279cef44048f2a75593c0a2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.11.1 This release

2 release files

1.11.0

2 release files

1.10.1

2 release files

1.10.0

2 release files

1.9.2

2 release files

1.9.1

2 release files

1.9.0

2 release files

1.8.3

2 release files

1.8.2

2 release files

1.8.0

2 release files

1.6.4

2 release files

1.6.3

1 release file

1.6.2

1 release file

1.6.1

1 release file

1.6.0

1 release file

1.4.8

2 release files

1.4.7

2 release files

1.4.4

1 release file

1.4.0

1 release file

1.3.6

1 release file

1.3.5

1 release file

1.3.4

1 release file

1.3.3

1 release file

1.3.2

1 release file

1.3.0

1 release file

1.2.2

1 release file

1.2.1

2 release files

1.2.0

1 release file

1.1.2

1 release file

1.1.1

1 release file

1.0.5

1 release file

1.0.3

1 release file

1.0.2

1 release file

1.0.0

1 release file

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page