- Dockerfile 100%
| .env.example | ||
| .gitignore | ||
| compose.yaml | ||
| Dockerfile | ||
| README.md | ||
Toolchain container
This directory is a toolchain container setup: Packer runs in an ephemeral Docker
container, while the actual project (.pkr.hcl files, plugins, etc.) lives in a sibling
directory on the host and is bind-mounted in.
hashicorp-packer/
├── packer-in-docker/ ← Compose, Dockerfile, .env (the runner)
└── insecure-user-with-docker/ ← Packer project (mounted into the container)
The container provides the toolchain (Packer version, shell, SSH keys). It is not a
long-running service beside the project — each command is a one-off docker compose run.
Configuration
The project name and the Packer working directory are driven by
COMPOSE_PROJECT_NAME. The default value lives in .env:
COMPOSE_PROJECT_NAME=insecure-user-with-docker
Compose uses the effective project name for:
- the Docker Compose project name
- the bind mount:
../${COMPOSE_PROJECT_NAME}on the host is mounted to/root/${COMPOSE_PROJECT_NAME}in the container
You can also set the project name at runtime. Precedence (highest to lowest):
-p/--project-nameon the command lineCOMPOSE_PROJECT_NAMEin the shell environmentCOMPOSE_PROJECT_NAMEin.env- the
name:field incompose.yaml(here:${COMPOSE_PROJECT_NAME})
A shell environment variable overrides the value from .env. Because the bind mount
uses ${COMPOSE_PROJECT_NAME} as well, an override changes both the Compose project
name and the mounted directory — the sibling folder must exist.
Examples:
# override for a single command
COMPOSE_PROJECT_NAME=other-project docker compose run --rm packer packer validate debian13.pkr.hcl
# same, via flag
docker compose -p other-project run --rm packer packer validate debian13.pkr.hcl
To switch projects permanently, change COMPOSE_PROJECT_NAME in .env to match the
sibling folder name under the repo root.
Project .env
Each mounted Packer project has its own .env for build secrets and cloud settings.
Copy .env.example to .env in the project directory (e.g.
insecure-user-with-docker/.env). Packer reads these variables via env() in
.pkr.hcl — export them in your shell or pass them when running compose, for example:
set -a && source ../insecure-user-with-docker/.env && set +a
docker compose run --rm packer packer build main.pkr.hcl
| Variable | Purpose |
|---|---|
HCLOUD_TOKEN |
Hetzner Cloud API token |
SSH_KEY_NAME |
Name of the SSH public key registered in Hetzner Cloud (see below) |
SSH keys
Cloud builders (e.g. Hetzner Cloud) create a temporary VM for each build. Packer SSHes into that VM to run provisioners, then snapshots the result. That requires a key pair:
| Piece | Location | Role |
|---|---|---|
| Private key | packer-in-docker/ssh/ → /root/.ssh in the container |
Packer uses this to authenticate from the toolchain container to the build VM |
| Public key | Registered in your cloud account (e.g. Hetzner Cloud) | The cloud provider installs this on the build VM when it is created |
| Key name | SSH_KEY_NAME in the mounted project's .env |
Name of the registered public key in Hetzner Cloud, passed to the builder (e.g. ssh_keys in .pkr.hcl) |
This key is for build-time access only. It is not baked into the final machine image unless your provisioners explicitly do that.
Use a dedicated build key here — not your personal SSH key.
One-time setup
-
Generate a key pair. Either in the container (recommended — OpenSSH is already installed) or on the host; both write to the same bind-mounted directory:
cd packer-in-docker mkdir -p ssh # in the container docker compose run --rm packer ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N "" -C "packer-build" # or on the host (equivalent) ssh-keygen -t ed25519 -f ssh/id_ed25519 -N "" -C "packer-build" -
Upload
ssh/id_ed25519.pubto Hetzner Cloud (console or API). Use the same name as the key comment — e.g.packer-build. -
Set
SSH_KEY_NAMEin the mounted project's.envto that name. For example, ininsecure-user-with-docker/.env:SSH_KEY_NAME=packer-build
The ssh/ directory is gitignored. Do not commit private keys.
Do not generate keys inside the Docker image (Dockerfile/docker build) — they would
be baked into the image. Keys must live in the bind-mounted ssh/ directory on the host.
Usage
Build the container image
docker compose build
Run packer
docker compose run --rm packer packer version
Build a machine image
docker compose run --rm packer packer init main.pkr.hcl
docker compose run --rm packer packer validate main.pkr.hcl
docker compose run --rm packer packer build main.pkr.hcl
Interactive shell in the mounted packer dir
docker compose run --rm packer fish # or ash