Deliver a secret to a Compose service
This tutorial adds an OpenVault agent to a Compose service, starts the service, and reads the delivered secret from the service's tmpfs directory. It uses the reference stack as the vault and a tiny Alpine container as the application.
1. Start the reference stack
From the repository root:
./deploy/compose/prepare.sh
export OPENVAULT_BASE_URL=https://localhost
export OPENVAULT_SITE_ADDRESS=localhost
docker compose -f deploy/compose/compose.yaml up -dRead the one-time bootstrap password and sign in:
install -m 0600 /dev/null /tmp/openvault-bootstrap
docker compose -f deploy/compose/compose.yaml cp \
openvault-init:/bootstrap/bootstrap-password /tmp/openvault-bootstrap
cat /tmp/openvault-bootstrap
rm -f /tmp/openvault-bootstrapOpen https://localhost and sign in as admin@openvault.local. Change the password before you continue if this stack will live longer than the tutorial.
2. Create the project and secret
Use the ov CLI from the release. If it is not on your PATH, run it from the server image. The commands below use ov:
export OPENVAULT_CA_FILE="$PWD/deploy/compose/data/ca/ca.pem"
ov login --url https://localhost --identifier admin@openvault.localov project create tutorial
printf 'tutorial-value-1' | ov secret set api-token --project tutorial --stdinThe secret is encrypted at rest. The agent will receive only the value named in its manifest.
3. Scaffold the agent
ov docker scaffold tutorial api \
--secret api-token \
--compose-project tutorial \
--out tutorialThe command writes two files and prints a one-time join token:
| File | Purpose |
|---|---|
tutorial/openvault.tutorial.api.compose.yaml | Agent and application service snippet |
tutorial/openvault.tutorial.api.manifest.yaml | The exact secret-to-file mapping |
Store the join token in a shell variable or an environment file outside the Compose file:
export OPENVAULT_JOIN_TOKEN='paste-the-token-here'The token is shown once. Re-running the scaffold command rotates it.
4. Prepare the host paths
The agent and the application run as UID/GID 10001 and share a host tmpfs directory. Create the directories before Compose starts; Docker would create missing bind sources as root.
export COMPOSE_PROJECT_NAME=tutorial
install -d -m 0700 -o 10001 -g 10001 \
"/dev/shm/openvault/tutorial/${COMPOSE_PROJECT_NAME}/api" \
"./tutorial/openvault-state/tutorial/${COMPOSE_PROJECT_NAME}/api"
install -m 0444 -o 10001 -g 10001 \
"deploy/compose/data/ca/ca.pem" \
"./tutorial/openvault-ca.pem"The generated Compose file references ./openvault-ca.pem and ./openvault.tutorial.api.manifest.yaml relative to the file's directory.
5. Add the application image
The generated api service has the volume and depends_on wiring but no image. Open tutorial/openvault.tutorial.api.compose.yaml and add a tiny application image and command:
services:
api:
image: alpine:3.21
command: ["sh", "-c", "cat /run/secrets/api-token; sleep 3600"]
# Keep the generated user, volumes, and depends_on below this block.The generated file ends with an external vault_net. The reference stack creates that network with the explicit name vault_net:
networks:
vault_net:
external: true
name: vault_net6. Start the service and read the file
docker compose -f tutorial/openvault.tutorial.api.compose.yaml up -d
docker compose -f tutorial/openvault.tutorial.api.compose.yaml ps
docker compose -f tutorial/openvault.tutorial.api.compose.yaml \
exec api cat /run/secrets/api-tokenYou should see tutorial-value-1. The application never talks to the vault; the agent fetched the value over mTLS and wrote the file.
Check the agent's status:
docker compose -f tutorial/openvault.tutorial.api.compose.yaml \
exec openvault-agent-api ov-agent health --readyExit code 0 means every required manifest entry is present. A missing required entry makes the agent remove its managed files and exit non-zero, so the application's depends_on health gate stops it.
7. Rotate and verify
Write a new version:
printf 'tutorial-value-2' | ov secret set api-token --project tutorial --stdinRestart the agent to fetch immediately, or wait for its refresh interval:
docker compose -f tutorial/openvault.tutorial.api.compose.yaml \
restart openvault-agent-api
docker compose -f tutorial/openvault.tutorial.api.compose.yaml \
exec api cat /run/secrets/api-tokenThe file now contains tutorial-value-2. The old version remains readable through the API until it is deleted or purged; the file delivery always uses the newest active version.
8. Clean up
docker compose -f tutorial/openvault.tutorial.api.compose.yaml down --remove-orphans
rm -rf "/dev/shm/openvault/tutorial/${COMPOSE_PROJECT_NAME}"
rm -rf ./tutorialThe reference stack is separate; stop it with docker compose -f deploy/compose/compose.yaml down when you are done.
What you learned
- The scaffold command creates the identity, the least-privilege grant, the manifest, and a one-time token.
- The agent is the only bridge from the application network to the vault.
- The application reads an ordinary file from a per-service tmpfs directory.
- Rotation is a new immutable version; the agent refreshes it on its interval.