Imported from zvchei/docker-seed (
AGENTS.md). Install upstream withnpx skills add zvchei/docker-seed. Copyright stays with the author.
DockerSeed — Agent Instructions
What this project is
DockerSeed is a code-generation tool, not a service itself. It generates Docker service files from composable templates and then orchestrates building the resulting stack.
Core workflow
Most scripts operate on the current working directory (Path.cwd()), not
the directory that contains the script. move.py instead takes an explicit
project directory and target path.
./till.py # scaffolds containers.json, .env (from .env.template), and
# related files in cwd; then runs configure.py
# (or in a given directory, created after confirmation)
./sow.py # reads ./containers.json; generates ./services/<name>/…
./tend.py # (optional) downloads ./assets.json → ./assets/
./harvest.py # regenerates ./docker-compose.yaml; if .env is missing,
# seeds it from .env.template and runs configure.py;
# optionally runs docker-compose build
./configure.py # interactively review/update .env (re-run anytime)
./transfer.py # copy a directory or file into or out of a named volume
# (service need not be running; direction from args)
./move.py # move a project directory; optionally migrate Docker
# volumes, image tags, and COMPOSE_PROJECT_NAME
./backup.py # archive containers.json/.env/templates/assets.json/
# secrets (opt-in) plus every project volume into one
# restorable .tar.gz
./restore.py # recreate a project from a backup.py archive, optionally
# under a new name (--name); regenerates via sow.py/harvest.py
move.py, backup.py, and restore.py preflight-check everything before
mutating anything, and roll back automatically on failure; if the rollback
itself fails, they write a *-ROLLBACK-<timestamp>.txt file with manual
recovery commands.
Built-in templates and the shared common/ tree live in the DockerSeed repository. When the working directory is not the repo, sow.py / harvest.py sync common/ into ./common/. Template lookup prefers ./templates/<name>/ over <repo>/templates/<name>/. The repo ships .env.template only; till.py / harvest.py copy it to ./.env when missing.
Generated files — never edit manually
These files are overwritten by the scripts above (in the working directory):
docker-compose.yaml(root) — generated byharvest.pyservices/<name>/Dockerfile— generated bysow.pyservices/<name>/docker-compose.yaml— generated bysow.pyassets.json— generated bysow.pyassets/<filename>— downloaded bytend.pycommon/— synced from the repo when working outside it
To persist changes to a service, edit containers.json or the relevant template under templates/ (local or repo).
Architecture
containers.json # (cwd) defines which services to generate and how
templates/<name>/ # (repo + optional cwd overlay) reusable building blocks
template.json # manifest: apt_packages, volumes, ports, cmd, etc.
root.Dockerfile # Dockerfile fragment run as root
user.Dockerfile # Dockerfile fragment run as the container user
common/
Dockerfile # base image (ubuntu:latest); all services use FROM localhost/base
docker-compose.yaml # base service definition included by the root compose
services/<name>/ # (cwd) generated output — do not edit
assets/ # (cwd) downloaded binary assets (from tend.py)
assets.json # (cwd) generated by sow.py; list of {url, filename} assets for tend.py
secrets/ # local secrets (ssh keys, tokens); excluded from the repository
.env.template # (repo only) default env values; till/harvest copy to cwd .env
The generated Dockerfiles extend common/Dockerfile and have two stages (root and user) to separate privileged setup from unprivileged runtime. The generated docker-compose.yaml extends common/docker-compose.yaml and adds service-specific settings.
Templates
Templates are reusable building blocks for services. Each template has a template.json manifest describing its properties (e.g. apt_packages, ports, cmd) and two Dockerfile fragments (root.Dockerfile and user.Dockerfile) that are merged into the generated service's Dockerfile.
Templates are composable and can depend on each other via the requires property in template.json. When a service includes multiple templates, their properties are merged according to the rules below.
Local ./templates/<name>/ overrides a same-named template under the repository templates/ directory (whole template directory wins; dependency resolution uses the same lookup).
template.json properties
| Property | Type | Description |
|---|---|---|
description |
string | Human-readable label for the template. Metadata only — not used during generation. |
requires |
list[string] | Other templates this template depends on. They are resolved and prepended automatically before merging. |
apt_packages |
list[string] | Debian packages installed via apt-get in the root Dockerfile stage. Deduplicated union across templates. |
assets |
list[{url, filename}] | Files to download with tend.py. Each entry has a url and a filename; the file is placed in assets/ and copied into the image at build time. |
volumes |
dict[name → host_path | {path, container_specific}] | Named volumes mounted into the container. Use string host_path for shared volume names or object form with container_specific: true to prefix volume names with <container>_ for private storage. Paths are relative to $HOME; directories are mkdir -p'd at build time. Deduplicated union across templates. |
env_vars |
dict[name → value] | Environment variables set at container runtime (environment: in compose). Merged; later templates overwrite earlier ones. |
build_args |
dict[name → value] | Docker build arguments declared as ARG and passed via args: in compose. Merged; later templates overwrite earlier ones. |
ports |
list[string] | Port mappings in "host:container" format. Deduplicated union across templates. |
cmd |
list[string] | Default container command (CMD). Last-wins when merging templates. |
entrypoint |
list[string] | Container entrypoint (ENTRYPOINT). Last-wins when merging templates. |
hostname |
string | Fixed hostname for the service in the compose network. Defaults to ${PROJECT}-<name> when omitted. |
init |
bool | When true, adds init: true to the compose service so an init process reaps zombie processes. |
contexts |
dict[name → host_path] | Additional Docker build contexts (additional_contexts: in compose build:). Used to inject host directories (e.g. SSH keys) at build time. |
Merge rules
The containers.json file defines services as ordered lists of template or service references. When sow.py is started, it resolves each source and merges its properties to generate the final Dockerfiles and compose files.
When sow.py merges multiple templates for a container:
- Lists (e.g.
apt_packages,ports,volumes) → deduplicated union; order preserved - Dicts (e.g.
env_vars,build_args) → merged; later template keys overwrite earlier ones requiresintemplate.json→ dependency resolution; required templates are resolved and prepended automatically- Service references in
containers.jsontemplates→ merge the referenced service's fully resolved configuration without reusing its image - Ambiguous names → qualify as
template:<name>orservice:<name>; a service's own unqualified name resolves to its same-named template - Direct container fields → applied last, after all entries in
templates - All others as described above.
containers.json conventions
- Use
_(not-) in container names — Docker Compose derives volume names from the name - Prefix a name with
@to define an abstract merge source that never generates a container, image, or compose include templatesentries can refer to template directories or services and are merged in order; abstract services are commonly referenced here- Set
"enabled": falseto skip a service without deletingservices/<name>/ - Use
"extends": "<name>"to reuse another service's image and settings without rebuilding - Abstract (
@) services cannot use or be targets ofextends - Use
"main": "<template>"to select which template'scmdwins as the default command - Command resolution order: explicit
cmd>maintemplate'scmd> last template with acmd "apt_packages","volumes","env_vars", and"build_args"can be specified directly on a container to add or override values from its templates (apt_packagesis a deduplicated union; the rest are merged dicts where container keys win)- See
examples_containers.jsonfor usage patterns.
Security defaults
Every generated service automatically gets mandatory security settings:
cap_drop: ["all"]
security_opt: [no-new-privileges]
read_only: true
tmpfs: [/tmp:noexec,nosuid,nodev,mode=1777]
Environment variables
The DockerSeed repo ships .env.template. Project directories use a concrete .env (gitignored). till.py and harvest.py seed .env from the template when it is missing; harvest.py skips prompting when .env already exists. Run ./configure.py anytime to walk through values again. Key variables consumed by all generated services:
CONTAINER_USER,CONTAINER_USER_ID— user identity inside containersPROJECT— project directory name under$HOMEGIT_AUTHOR_EMAIL,GIT_AUTHOR_NAME— passed as build args to every service
Python code style
sow.py and tend.py use Python 3.12+ features (type aliases, | unions). Type annotations are used throughout. The TypedDict Template and type aliases (Manifest, Fragment, Merged) are defined at the top of sow.py.
Commit style
Use a single short imperative sentence as the subject line — no conventional-commit prefixes (fix:, feat:, etc.). Close issues inline when applicable: (closes #N). Example:
Remove setup.sh references, superseded by assets system (closes #14)
