English | 简体中文
🧩 Pack your Compose, ship it anywhere.
The power of Helm-style configuration for Docker Compose.
Docker Compose is the standard for running multi-container applications, but it lacks templating, dynamic configuration, and clean packaging. Teams often struggle with giant YAML files, manual copy-pasting across environments, and complex .env file management.
ComposePack fixes this.
It brings a modern templating engine, overridable config system, and a packaging workflow to Docker Compose—while remaining 100% compatible with the standard Docker CLI.
- 📝 Go-Style Templates: Write dynamic Compose files with logic (
if,range, etc.). - ⚙️ Structured Config: Ship clean
values.yamldefaults and allow user overrides. - 📦 Real Packaging: Distribute your app as a versioned, installable
.cpackchart. - 🔐 Reproducible Releases: Render isolated, self-contained release directories.
- 🚀 Native Runtime: Under the hood, it's just
docker compose.
| Feature | Docker Compose | ComposePack |
|---|---|---|
| Templating | ❌ (Variables only) | ✅ (Full logic) |
| Config Model | ❌ (Flat .env) | ✅ (Values.yaml) |
| Packaging | ❌ | ✅ (Charts) |
| Environment Isolation | ❌ | ✅ (Release Dirs) |
| Drift Detection | ❌ | ✅ (Diff command) |
| Runtime Engine | ✅ | ✅ |
- ⚡ Quick 60-Second Demo
- 📦 Installation
- 🧠 How It Works (High-Level)
- 🚀 How to Use
- 🧩 Template Basics
- 📂 Chart Layout & File Types
- 🏗️ Runtime Layout
- 📏 Runtime Rules & Gotchas
- 📝 FAQ
- 🤝 Contributing
# 1. Scaffold a chart
composepack init charts/demo --name demo --version 0.1.0
# 2. Install it into a release with your own values
composepack install charts/demo --name myapp -f values-prod.yaml --auto-start
# 3. Watch logs
composepack logs myapp --followThat’s it: templated config + reproducible runtime on top of plain Docker Compose.
ComposePack is a single binary with no external dependencies other than Docker & Docker Compose.
- If you’ve tapped
composepack/tapalready:
brew install composepack- First time using the tap:
brew tap composepack/tap
brew install composepackNote: once accepted to homebrew-core, brew install composepack will work without a tap on macOS and Linux.
For use in GitHub Actions, GitLab CI, or other containerized environments:
# Use the official image
docker run --rm garearc/composepack:latest --version
# Or pin to a specific version
docker run --rm garearc/composepack:v1.0.0 --versionExample GitHub Actions workflow:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install and run composepack
run: |
docker run --rm \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
garearc/composepack:latest \
install charts/myapp --name prodThe image is multi-arch (supports linux/amd64 and linux/arm64) and is automatically published to Docker Hub on every release.
curl -fsSL https://raw.githubusercontent.com/composepack/composepack/main/scripts/install.sh | bash- Installs to
/usr/local/bin/composepackor falls back to~/.local/bin/composepack - Override install directory with
COMPOSEPACK_INSTALL_DIR
Uninstall:
./scripts/uninstall.sh./scripts/install.ps1 -Version v1.0.0 -InstallDir "$env:ProgramFiles\ComposePack"Uninstall:
./scripts/uninstall.ps1git clone https://github.com/composepack/composepack.git
cd composepack
make buildmake generate runs go generate ./... for Wire and other providers.
- You define a chart (templated Compose + assets).
- Users pass values (
values.yaml,--set, env vars). - ComposePack renders everything into a self-contained release directory.
- Docker Compose runs exclusively from that directory.
ComposePack has two kinds of users:
- Chart Creators (Shippers) → build and package charts
- Chart Users (Consumers) → install and run charts
Below is the workflow for each.
(for teams packaging their applications)
composepack init charts/example --name example --version 0.1.0This generates something like:
charts/example/
Chart.yaml
values.yaml
templates/
compose/00-app.tpl.yaml
files/config/message.txt.tpl
helpers/_helpers.tpl
files/
config/
composepack template dev --chart charts/exampleThis renders templates but does not create or modify a release.
composepack install charts/example --name dev --auto-startThis builds .cpack-releases/dev/ and runs docker compose up inside it.
composepack package charts/example --destination dist/Creates:
dist/example-0.1.0.cpack.tgz
You can also customize the output name:
composepack package charts/example --output dist/example.cpack.tgzYou can host that .cpack.tgz on HTTP(S), ship it as an artifact, or check it into your internal distribution system.
(for customers or internal developers consuming charts)
composepack install example.cpack.tgz --name myapp -f custom-values.yaml --auto-startinstall accepts:
- A local
.cpack.tgzarchive - A local chart directory
- An HTTP/HTTPS URL pointing to a packaged chart
composepack up myapp
composepack down myapp --volumes
composepack logs myapp --follow
composepack ps myapp
composepack template myapp
composepack diff myapp --chart <chart-source>All runtime files for this release live in:
.cpack-releases/myapp/
docker-compose.yaml
files/
release.json
If needed, you can cd into this folder and run docker compose manually.
Want to run these commands from somewhere else? Pass --runtime-dir to point directly at the release folder:
composepack up myapp --runtime-dir /opt/releases/myapp
composepack logs myapp --runtime-dir /opt/releases/myapp --followBefore running install or up, you can see what would change:
# If release exists, chart source is auto-resolved
composepack diff myapp
# Or compare against a different chart
composepack diff myapp --chart charts/myapp-v2This shows:
- What services would be added, removed, or modified
- Changes to the docker-compose.yaml configuration
- File differences (with
--show-files)
Example output:
📝 Docker Compose Changes:
- image: myapp:v1.0
+ image: myapp:v2.0
⚠️ Affected Services:
• myapp-api (modified)
Note: In the examples above, myapp is the release name (the name you gave to your deployment). The chart source is auto-resolved from the release metadata, so you typically don't need to specify --chart unless comparing against a different chart.
This helps you answer: "Will running install now restart my database?"
ComposePack uses Go templates — the same templating style many Helm users already know.
Example:
# templates/compose/00-app.tpl.yaml
services:
app:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
environment:
DB_HOST: "{{ .Values.db.host }}"
DB_PASSWORD: "{{ env "DB_PASSWORD" }}"You have access to:
.Values— merged system + user values.Env— environment variables.Release— name, version, metadata- Standard Go template functions (
default,include,quote,toJson, etc.)
If your team already uses Helm templates, the learning curve is almost zero.
This section explains what lives where inside a chart and how ComposePack treats those files.
A typical chart looks like this:
myapp/
Chart.yaml
values.yaml
templates/
compose/
00-app.tpl.yaml
10-worker.tpl.yaml
files/
config/app.env.tpl
helpers/
_helpers.tpl
files/
config/
scripts/
-
Required
-
Metadata about the chart:
name: string (required)version: string (required)description: stringmaintainers: []string
-
Used by ComposePack to identify the chart and write
release.json.
- Required
- Default system-level configuration for the chart.
- Users can layer their own
values-*.yamlor--setoverrides on top. - Think of this as “what the product ships with” vs “what users customize.”
-
Required folder
-
Each file is a templated Docker Compose fragment.
-
Must end with
.tpl.yaml. -
ComposePack:
- Renders these templates using
.Valuesand.Env. - Merges all rendered fragments into a single
docker-compose.yamlper release.
- Renders these templates using
Example:
templates/compose/
00-core.tpl.yaml
10-db.tpl.yaml
20-api.tpl.yaml
-
Optional.
-
Templated runtime assets:
- config files
- shell scripts
- anything written to disk for containers to mount.
-
Must end with
.tpl. -
ComposePack:
- Renders them
- Drops the
.tplsuffix - Writes them into the release’s
files/directory.
Example:
templates/files/
config/app.env.tpl -> files/config/app.env
scripts/init.sh.tpl -> files/scripts/init.sh
- Optional.
- Reusable template snippets and helper functions.
- Included via
{{ include "helper.name" . }}from other templates.
Example:
templates/helpers/_helpers.tpl
{{- define "myapp.fullname" -}}
{{ printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }}
{{- end -}}-
Optional.
-
Static assets that do not need templating.
-
Everything under
files/is copied as-is into the release’sfiles/directory. -
Good for:
- static config
- certificates
- seed data
- scripts that never change
Example:
files/
config/defaults.json
scripts/migrate.sh
In the release:
.cpack-releases/<name>/
files/config/defaults.json
files/scripts/migrate.sh
For each release, ComposePack maintains a self-contained directory:
.cpack-releases/<release>/
docker-compose.yaml # merged compose file (all fragments combined)
files/ # rendered & static assets referenced in templates
config/...
scripts/...
release.json # metadata: chart, version, values, environment, etc.
This is the only place Docker Compose runs from for that release.
These are important design rules to keep your charts predictable and easy to debug.
In the release directory, all non-compose assets live under files/.
That means all local volume paths in your Compose templates must be under ./files/....
Example:
# inside templates/compose/*.tpl.yaml
services:
app:
volumes:
- ./files/config/app.env:/app/app.env:ro
- ./files/scripts/init.sh:/docker-entrypoint.d/init.sh:roIf you reference paths outside ./files/, your containers may fail to start because those files won’t exist in the runtime directory.
ComposePack relies on file suffixes to decide how to treat files:
-
Compose templates must end with
.tpl.yaml- Example:
10-api.tpl.yaml
- Example:
-
Other templated files must end with
.tpl- Example:
app.env.tpl,init.sh.tpl
- Example:
-
Static assets that need no templating → put them under
files/without.tpl.
If the suffixes are wrong, files may be copied without rendering or ignored as compose fragments.
ComposePack always runs Docker Compose from the release’s directory:
.cpack-releases/<release>/
docker-compose.yaml
files/
When you run:
composepack up myappit is equivalent to:
cd .cpack-releases/myapp
docker compose -f docker-compose.yaml upYou must cd into the correct directory that contains .cpack-releases or specify the right --runtime-dir, otherwise, ComposePack won’t see the right file and volumes.
No. ComposePack wraps Docker Compose, it doesn’t replace it.
- ComposePack handles: templating, values, chart packaging, release directories
- Docker Compose handles: actually running the containers
You can always cd into .cpack-releases/<name>/ and run docker compose manually if you prefer.
ComposePack uses three different types of names:
- Chart Name - The name of the chart package (from
Chart.yaml). Example:example - Release Name - The name you give to a deployment instance (via
--nameflag). Example:myapp,prod,staging - Chart Source - The path/URL where the chart is located. Example:
charts/example,app.cpack.tgz
In practice:
- When you run
composepack install charts/example --name myapp:charts/example= chart source (where the chart is)example= chart name (from Chart.yaml)myapp= release name (what you call this deployment)
- After installation, you use the release name in commands:
composepack up myapp(notcomposepack up example)composepack diff myapp(chart source is auto-resolved)
You can deploy the same chart multiple times with different release names (e.g., prod, staging, dev).
See docs/naming.md for detailed explanation.
.env is fine for small projects, but it has limits:
- It’s flat (no nested structure)
- It doesn’t distinguish system defaults from user overrides (chaining multiple
.envfiles is super confusing and error-prone) - It’s hard to maintain across upgrades
- You can’t easily ship a “product config” separate from customer config
ComposePack gives you:
- default
values.yamlfor system defaults - user can specify their own
values-*.yamland--setoverrides on top - a clear separation between what you ship and what users customize
- a reproducible release directory for each environment
Helm is great — but it’s designed for Kubernetes, not Docker Compose.
Use Helm when:
- You’re deploying to a Kubernetes cluster
- You’re already invested in K8s tooling and workflows
Use ComposePack when:
- You want Helm-style templating and chart packaging
- You want to stay on plain Docker Compose
- You don’t want the operational overhead of running Kubernetes
Think of ComposePack as bringing the Helm experience to the Compose world.
Yes. ComposePack writes everything into:
.cpack-releases/<release>/
docker-compose.yaml
files/
release.json
If you prefer, you can:
cd .cpack-releases/<release>
docker compose up
docker compose ps
docker compose logsComposePack just standardizes how that directory is built.
Right now the project is early-stage and evolving.
It’s suitable for:
- Experimentation
- Internal tools
- Early adopters who are comfortable reading Go code and contributing
If you rely on it in production, please:
- Open issues for any problems
- Share feedback on missing features
- Consider contributing bugfixes or docs improvements 🙏
We welcome issues and PRs!
make fmt
make test
make build
make generate- CI (
.github/workflows/ci.yml) runs formatting, vetting, and tests on PRs and pushes tomain. - Tag releases with:
git tag vX.Y.Z
git push --tagsThis triggers cross-compiled binaries to be uploaded to GitHub Releases.
If you find ComposePack useful, consider ⭐ starring the repo — it helps others discover it!