[ English | 한국어 ]
Project homepage: https://allbegray.github.io/compman/
compman manages Docker or Podman Compose stacks from one CLI: run and inspect
services, back up and restore volumes and images, schedule unattended backups,
deploy releases from S3 or HTTPS, and post a Slack notification when a stack
comes up.
No daemon, no web UI, no port opened — it calls the Docker or Podman CLI you already have, which is why it fits locked-down hosts, jump boxes, and networks where a management platform cannot be installed.
- Auto-detects Docker Compose, Podman Compose,
podman-compose, anddocker-compose - Profile-based
composeconfig with per-profile env vars and AWS Secrets Manager injection - Project-scoped
psandstats, plusstatus/doctoras text or JSON for CI - Timestamped volume and image backups, gzip by default or Zstandard with
--zstd - Local, S3, or SSH/SCP backup storage, plus scheduled jobs via launchd/systemd/cron/schtasks
- Deploys from an S3 prefix or HTTP(S) archive with SHA-256 pinning and HTTPS header auth
- Slack notification on
stack up/stack updatewith unhealthy-service and volume detail - English and Korean help, shell completion, Windows/macOS/Linux
- Python 3.12+ (
--zstdneeds 3.14+, which ships the stdlibcompression.zstd) - Docker Compose or Podman Compose
- For S3: reachable S3-compatible storage and AWS credentials
- For authenticated HTTP deploys: an HTTPS archive URL and a token in an environment variable
CI runs Python 3.12–3.14 on Ubuntu, macOS, and Windows. Wheels publish to PyPI on every tagged release.
uv tool install compman # recommended, self-contained
pipx install compman # equivalentOr run the bundled installer, which puts the CLI on your PATH:
curl -fsSL https://raw.githubusercontent.com/allbegray/compman/main/install.sh | sh # Linux / macOS
irm https://raw.githubusercontent.com/allbegray/compman/main/install.ps1 | iex # Windows PowerShellThen compman --version. Use compman upgrade to refresh an installed tool in
place. If the installation is damaged, uninstall and reinstall from the
unpinned upstream source so future upgrades keep working:
uv tool uninstall compman
uv tool install --managed-python git+https://github.com/allbegray/compman.gitFor a development checkout, use uv tool install . and run the CLI from the repo.
cd my-project
compman init --scaffold # or run `compman init` for an interactive menu
compman stack up
compman statusTo deploy into an empty directory, compman deploy scaffolds the config for you:
mkdir my-app && cd my-app
compman deploy --path s3://my-bucket/releases/app.tar.gz --build --tag my-app
compman stack upmy-app/
├── compman.yml
├── docker-compose.yml
└── project/ # source fetched from S3/HTTP
Everything lives under the compman key in compman.yml. A JSON Schema is
published at docs/site/compman.schema.json —
add this first line for IDE autocomplete and validation:
# yaml-language-server: $schema=https://allbegray.github.io/compman/compman.schema.jsonWorked examples: examples/compman-config/ (index in
examples/README.md).
compose is required and must be a mapping of profiles. One profile is
enough; add more to switch environment per deployment.
compman:
name: my-stack
compose:
base: docker-compose.yml
dev:
file: docker-compose.dev.yml
env:
DATABASE_URL: dev.db.example.com
prod:
file: docker-compose.prod.yml
env:
DATABASE_URL: prod.db.example.comcompman stack up devA profile file is optional: omitted, it falls back to base, then
docker-compose.yml. That lets one Compose file vary only by environment.
| Key | Purpose |
|---|---|
name |
Stack name; defaults to the config directory name |
folder |
Subdirectory holding the Compose files |
compose |
Required. Profile mapping; base: adds a shared Compose file |
dirs.project |
Managed deployment source tree |
dirs.backup |
Archive destination: local path, s3://bucket/prefix, or ssh://[user@]host[:port]/path |
dirs.volume |
Scratch directory for host↔container volume transfers |
deploy |
Default source for deploy/update: s3://…, an HTTPS archive URL, or a mapping (below) |
secrets |
AWS Secrets Manager entries, referenced by ${secrets:NAME} |
notify.slack |
Slack webhook settings (see Notifications) |
limits.max_archive_mb |
Cap on fetched deploy source size |
limits.max_backups |
Keep only the newest N archives per stack and kind |
Managed paths resolve relative to the config directory and may never escape it.
--path overrides deploy for a single invocation.
Declare { arn, key } pairs under secrets, then reference them from a profile
env with ${secrets:NAME}. compman substitutes the value at key when it
builds a compose context and passes the result to the docker compose process
environment, so the Compose file still uses ${VAR}.
compman:
name: my-stack
compose:
default:
file: docker-compose.yml
dev:
file: docker-compose.dev.yml
env:
DATABASE_URL: postgres://${secrets:DB_USER}@db.example.com
secrets:
DB_USER:
arn: arn:aws:secretsmanager:ap-northeast-2:123456789012:secret:db
key: dtx/db/user- Injected only where a marker appears, never as standalone compose variables. An undeclared marker name fails the command.
keymay be a slash path. Partial interpolation works, and markers can sit beside system-variable references.- A profile
secretsblock wins over the top-level one. Each ARN is fetched once per invocation. compman doctorwarns when credentials or region are missing.
S3 accepts a prefix (recursive, structure preserved) or a archive
(.tar.gz, .tgz, .zip, with a single top-level directory flattened).
Public HTTP/HTTPS accepts archives only, and the path must end in one of those
suffixes. Only the target with the same name is replaced; your own files are kept.
The mapping form adds pinning and auth:
compman:
deploy:
url: s3://my-bucket/releases/app.tar.gz
sha256: <64-hex-digest>
auth: { header: Authorization, value_env: DEPLOY_TOKEN } # HTTPS only- Transactional build.
--buildcompiles from the temporary source before the managed-tree swap, so a build failure leaves the existing tree and config untouched. A failed swap rolls back. - Integrity pinning.
--sha256 HEXordeploy.sha256is verified after download, before extraction, build, and swap. A mismatch aborts with exit 1 and changes nothing. It applies whenever the deployed URL equals the configureddeployURL, soupdateinherits it. - Token handling. The header value is read at fetch time from
value_env— never stored, never echoed, errors name only the variable. It is sent verbatim, so for Bearer auth store the fullBearer <token>string. - HTTPS required for auth, and the header is dropped on a cross-host redirect so the token cannot leak. Serve from one host if your CDN needs it after redirecting.
compman rollbackrestores the snapshot from the previous successful deploy.
compman init [--scaffold | --s3 URI | --seed]
compman deploy [--path SOURCE_URI] [--sha256 HEX] [--build] [--tag TAG]
compman update [PROFILE] [-c|--config PATH] [--stack NAME]
compman rollback
compman doctor [--profile PROFILE] [-c|--config PATH] [--json] [--stack NAME]
compman status [--profile PROFILE] [-c|--config PATH] [--json] [--stack NAME]
compman ps [PROFILE] [-a|--all] [--json] [-c|--config PATH] [--stack NAME]
compman stats [PROFILE] [-f|--follow] [--json] [-c|--config PATH] [--stack NAME]
compman history [--limit N] [--json]
compman stacks list [--json]
compman stacks remove NAME
compman clear [--yes]
compman upgrade [--repo URL]
compman lang [ko|en]
compman version
compman completion [powershell|bash|zsh|fish] --install
compman stack up [PROFILE] [--wait] [-c|--config PATH] [--stack NAME]
compman stack update [PROFILE] [--wait] [-c|--config PATH] [--stack NAME]
compman stack down [--profile PROFILE] -c|--config PATH --yes [--stack NAME]
compman stack logs [SERVICE...] [-f] [--tail N] [--profile PROFILE] [-c|--config PATH]
compman service start|stop|restart [SERVICE...] [--profile PROFILE] [-c|--config PATH]
compman service status [--profile PROFILE] [-c|--config PATH]
compman service log [SERVICE] [-f] [-n 50] [--profile PROFILE] [-c|--config PATH]
compman service connect [SERVICE] [--profile PROFILE] [-c|--config PATH]
compman volume backup [-z LEVEL] [--zstd] [--no-stop] [--profile PROFILE] [-c|--config PATH]
compman volume restore [TIMESTAMP] [--no-stop] [--replace] [--profile PROFILE] [-c|--config PATH]
compman volume pull [--profile PROFILE] [-c|--config PATH]
compman volume push [--replace] [--profile PROFILE] [-c|--config PATH]
compman image backup [-z LEVEL] [--zstd] [--source-image] [--profile PROFILE] [-c|--config PATH]
compman image restore [TIMESTAMP] [--profile PROFILE] [-c|--config PATH]
compman schedule add [--every N | --daily HH:MM | --weekly DAY HH:MM | --monthly DD HH:MM] [--no-stop] [-z LEVEL] [--name TEXT] [--scheduler systemd|cron]
compman schedule list [--json]
compman schedule status NAME
compman schedule remove NAME
-c/--config selects the config file, --profile the compose profile, and the
global --stack NAME runs any command against a registered stack from any
directory. View all options for any command with compman <command> --help.
updateis a rebuild plus force-recreate — not a zero-downtime rolling deploy. Withoutdeployit runsup -d --buildlocally.stack downon a missing stack is not an error: it exits 0, so scripts stay idempotent.psandstatsare project-scoped by design.docker ps/podman psfor runtime-wide results.service logandconnecttake service names, resolved viacompose ps -q. Zero instances, or a scaled service with several, fails with guidance rather than guessing.connectfalls back tosh.volume backup/restorestop the stack for consistency;--no-stopopts out. Restoring while everything is stopped also works — the stack is started temporarily and stopped again.volume restore/push --replaceis a byte-for-byte replace that deletes destination-only files. Destructive by design; the destination must be an absolute container path.image backupcommits container state unless--source-imageis passed. gzip level defaults to 6 (-z);--zstdwrites.tar.zstand needs Python 3.14+ for restore too.- Archives are named
<stack>.{volume,image}.<YYYYMMDD_HHMMSS>[_<microseconds>].tar.gz. clearrunsimage prune -af, which can remove unused images outside this project. Requires--yes.- Long operations time out after 300s; override with
COMPMAN_TIMEOUT=<seconds>. Streaming commands (log -f,connect,stats -f) never time out.
compman doctor
compman doctor --json
compman status --json--json emits schema version 1. doctor exits 1 only when a required check
fails; missing AWS credentials, an unpinned deploy, and unset
deploy.auth/notify.slack variables are warnings. status exits 1 when the
stack is missing, and 0 when it exists even if every service is stopped.
Operational failures, including Docker Desktop readiness on Windows, print as concise messages without a Python traceback.
Set the webhook and every stack reports on stack up and stack update:
export COMPMAN_SLACK_WEBHOOK_URL='https://hooks.slack.com/services/T000/B000/XXXX'Or scope it per stack so the secret stays out of the environment of the calling shell:
compman:
notify:
slack:
webhook_env: COMPMAN_SLACK_WEBHOOK_URL # recommended
# webhook: https://hooks.slack.com/services/... # literal URL, keeps the secret in this file✅ Stack started
*Stack* notify-demo · *Profile* default
*Runtime* docker · *Host* build-host
*Started at* 2026-10-02 20:06 KST · *Duration* 681ms
*Services* — 3 of 3 healthy
────────────────────────────────────────
*Volumes* (2)
🟢 notify-demo_web-data · web:/usr/share/nginx/html · 1.393kB
🟢 notify-demo_pgdata · db:/var/lib/postgresql/data · 40.01MB
compman 1.12.0 · stack up
Resolution order is webhook, then the variable named by webhook_env, then
COMPMAN_SLACK_WEBHOOK_URL — the last only when the file has no notify.slack
block. A stack naming its own variable never falls back to the global one.
Services appear as a healthy count plus only the ones that are not healthy,
each with state, exit code, image tag, and published ports. The headline turns
into ⚠️ … N service(s) need attention as soon as anything is off, and a volume
used by a failing service is marked 🔴 to point at the cause.
Delivery is best-effort and never changes the exit status — the containers are
already running, so an outage, a revoked webhook, or an unset variable warns on
stderr and still exits 0. stack down, backup, and restore do not notify, so
the temporary restarts around a backup stay silent.
Volume sizes come from docker system df -v, the only Docker surface that
reports them. It scans the whole host, so compman runs it only when the compose
files declare a named volume and notifications are enabled; otherwise volumes
are still listed with their mount paths and only the sizes are dropped.
dirs.backup takes a local path, s3://bucket/prefix, or
ssh://[user@]host[:port]/path. Remote stores stage locally, upload, verify, then
remove the staged copy; SSH mode drives scp/ssh with BatchMode=yes and
assumes keys are already provisioned.
compman schedule add --daily 04:30 --no-stop
compman schedule add --every 30m
compman schedule add --monthly 1 05:00
compman schedule status my-stack.volume
compman schedule remove my-stack.volumeExactly one cadence option is required. The platform mechanism is chosen
automatically: launchd on macOS, schtasks on Windows, and a systemd user timer or
crontab on Linux (--scheduler systemd|cron forces the Linux choice). Cron cannot
express every interval — --every must divide 60 minutes or be whole hours.
Output and per-run records land beside schedules.json under %APPDATA%\compman
when APPDATA is set, otherwise ~/.config/compman; see
docs/site/ for the registry layout.
Detection order is docker compose → podman compose → podman-compose →
docker-compose. Force one with CONTAINER_RUNTIME=podman.
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_DEFAULT_REGION=ap-northeast-2
export AWS_ENDPOINT_URL_S3=http://localhost:4566 # Ministack/LocalStack
export COMPMAN_TIMEOUT=600 # seconds; default 300
export COMPMAN_LANG=ko # or --lang ko per invocationOn Windows with Docker, stack up, stack update, and deploy builds check
Docker Desktop readiness and offer to start it. Non-interactive runs never launch
it, and read-only, backup, and down paths skip the check entirely.
| Document | Contents |
|---|---|
| examples/compman-config/ | 14 case-by-case compman.yml files |
| CHANGELOG.md | Release history |
| SECURITY.md | Credential model, secret handling, reporting |
| BACKLOG.md | Constraints and open work |
| AGENTS.md | Development, testing, and release rules |
| SOLUTION.md | Debugging and design lessons |
| Homepage | Search-optimized project site |
MIT — see LICENSE.