Configure Podman secrets management with HashiCorp Vault integration

Advanced 45 min Apr 22, 2026 23 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up secure container secrets management by integrating Podman with HashiCorp Vault. Configure dynamic secret injection, automated rotation, and production-ready monitoring for containerized applications.

Prerequisites

  • Root or sudo access
  • Basic Podman knowledge
  • Understanding of container security
  • Network connectivity for package installation

What this solves

Container secrets management becomes critical when running production workloads that need database credentials, API keys, and certificates. This tutorial shows you how to integrate Podman with HashiCorp Vault to automatically inject secrets into containers, rotate credentials, and monitor access patterns without hardcoding sensitive data in images or environment variables.

Step-by-step configuration

Install HashiCorp Vault

Download and install the official Vault binary with GPG verification for secure package management.

wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install -y vault
sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo dnf install -y vault

Configure Vault server

Create the Vault configuration file with file storage backend and API listener for development and testing environments.

sudo mkdir -p /opt/vault/data /etc/vault.d
sudo useradd --system --home /opt/vault --shell /bin/false vault
sudo chown -R vault:vault /opt/vault
ui = true
disable_mlock = true

storage "file" {
  path = "/opt/vault/data"
}

listener "tcp" {
  address     = "127.0.0.1:8200"
  tls_disable = 1
}

api_addr = "http://127.0.0.1:8200"
cluster_addr = "https://127.0.0.1:8201"

Create Vault systemd service

Set up Vault as a system service with proper security isolation and automatic startup configuration.

[Unit]
Description=HashiCorp Vault
Documentation=https://www.vaultproject.io/docs/
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/vault.d/vault.hcl
StartLimitIntervalSec=60
StartLimitBurst=3

[Service]
Type=notify
User=vault
Group=vault
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
PrivateDevices=yes
SecureBits=keep-caps
AmbientCapabilities=CAP_IPC_LOCK
Capabilities=CAP_IPC_LOCK+ep
CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK
NoNewPrivileges=yes
ExecStart=/usr/bin/vault server -config=/etc/vault.d/vault.hcl
ExecReload=/bin/kill --signal HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
StartLimitInterval=60
StartLimitBurst=3
LimitNOFILE=65536
LimitMEMLOCK=infinity

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now vault

Initialize and unseal Vault

Initialize the Vault server and configure the root token for administrative access.

export VAULT_ADDR='http://127.0.0.1:8200'
vault operator init -key-shares=1 -key-threshold=1
Important: Save the unseal key and root token in a secure location. You'll need them to unseal Vault after restarts and for administrative operations.
vault operator unseal [UNSEAL_KEY_FROM_INIT]
export VAULT_TOKEN="[ROOT_TOKEN_FROM_INIT]"

Enable KV secrets engine

Enable the key-value secrets engine version 2 for storing application secrets with versioning support.

vault secrets enable -path=secret kv-v2
vault secrets list

Install Podman secrets provider

Install the vault-plugin-secrets-podman plugin to enable direct integration between Podman and Vault.

sudo apt install -y podman
wget https://github.com/containers/podman/releases/download/v4.8.0/podman-4.8.0-linux-amd64.tar.gz
tar -xzf podman-4.8.0-linux-amd64.tar.gz
sudo dnf install -y podman
wget https://github.com/containers/podman/releases/download/v4.8.0/podman-4.8.0-linux-amd64.tar.gz
tar -xzf podman-4.8.0-linux-amd64.tar.gz

Configure Podman secrets driver

Configure Podman to use Vault as the secrets driver for automatic secret injection into containers.

mkdir -p ~/.config/containers
[storage]
driver = "overlay"
runroot = "/run/user/1000/containers"
graphroot = "/home/user/.local/share/containers/storage"

[storage.options]
additionalimagestores = [
]

[storage.options.overlay]
mountopt = "nodev,metacopy=on"

Create Vault authentication policy

Set up an authentication policy and token for Podman to access secrets from Vault with minimal required permissions.

vault policy write podman-secrets - <
vault token create -policy=podman-secrets -ttl=24h

Store application secrets in Vault

Create sample application secrets in Vault that will be injected into containers at runtime.

vault kv put secret/app/database \n    username="appuser" \n    password="secure-db-password" \n    host="db.example.com" \n    port="5432"

vault kv put secret/app/api \n    key="sk-1234567890abcdef" \n    endpoint="https://api.example.com" \n    timeout="30s"

Configure Vault agent for Podman

Set up Vault agent to automatically authenticate and provide secrets to Podman containers.

vault {
  address = "http://127.0.0.1:8200"
}

auto_auth {
  method "token_file" {
    config = {
      token_file_path = "/home/user/.vault-token"
    }
  }

  sink "file" {
    config = {
      path = "/tmp/vault-token-via-agent"
    }
  }
}

template {
  source      = "/etc/vault-templates/database.tpl"
  destination = "/tmp/database-config.json"
  perms       = 0644
}

template {
  source      = "/etc/vault-templates/api.tpl"
  destination = "/tmp/api-config.json"
  perms       = 0644
}

Create secret templates

Define templates that format Vault secrets for consumption by your containerized applications.

sudo mkdir -p /etc/vault-templates
{{- with secret "secret/data/app/database" -}}
{
  "username": "{{ .Data.data.username }}",
  "password": "{{ .Data.data.password }}",
  "host": "{{ .Data.data.host }}",
  "port": "{{ .Data.data.port }}",
  "connection_string": "postgresql://{{ .Data.data.username }}:{{ .Data.data.password }}@{{ .Data.data.host }}:{{ .Data.data.port }}/myapp"
}
{{- end -}}
{{- with secret "secret/data/app/api" -}}
{
  "api_key": "{{ .Data.data.key }}",
  "endpoint": "{{ .Data.data.endpoint }}",
  "timeout": "{{ .Data.data.timeout }}"
}
{{- end -}}

Start Vault agent

Start the Vault agent to begin secret templating and automatic token renewal.

echo "[PODMAN_TOKEN_FROM_STEP_8]" > ~/.vault-token
vault agent -config=/etc/vault-agent.hcl &

Create container with Vault secrets

Run a Podman container that mounts the dynamically generated secret files from Vault.

podman run -d \n  --name myapp \n  --mount type=bind,source=/tmp/database-config.json,target=/app/database.json,readonly \n  --mount type=bind,source=/tmp/api-config.json,target=/app/api.json,readonly \n  --env-file /dev/null \n  alpine:latest \n  sh -c "while true; do cat /app/database.json; sleep 30; done"

Configure secret rotation

Set up automatic secret rotation using Vault's database secrets engine for dynamic credential generation.

vault secrets enable database

vault write database/config/postgresql \n    plugin_name=postgresql-database-plugin \n    connection_url="postgresql://{{username}}:{{password}}@db.example.com:5432/postgres?sslmode=disable" \n    allowed_roles="readonly" \n    username="vault-admin" \n    password="admin-password"
vault write database/roles/readonly \n    db_name=postgresql \n    creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \n    default_ttl="1h" \n    max_ttl="24h"

Set up secret monitoring

Configure Vault audit logging and metrics collection to monitor secret access patterns and usage.

sudo mkdir -p /var/log/vault
sudo chown vault:vault /var/log/vault
vault audit enable file file_path=/var/log/vault/audit.log
vault write sys/metrics/config enabled=true
vault read sys/metrics/config

Create monitoring script

Set up a monitoring script to track secret usage and rotation status for operational visibility.

#!/bin/bash
set -euo pipefail

export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN="$(cat ~/.vault-token)"

echo "=== Vault Status ==="
vault status

echo "=== Active Leases ==="
vault list sys/leases/lookup/database/creds/readonly || echo "No active database leases"

echo "=== Recent Audit Events ==="
tail -n 10 /var/log/vault/audit.log | jq -r '.time + " " + .request.operation + " " + .request.path'

echo "=== Token Info ==="
vault token lookup -format=json | jq -r '.data | "TTL: " + (.ttl|tostring) + "s, Policies: " + (.policies|join(","))'

echo "=== Secret Access Count (last hour) ==="
grep "$(date -d '1 hour ago' +'%Y-%m-%dT%H')" /var/log/vault/audit.log | grep '"secret/data/app"' | wc -l
sudo chmod +x /usr/local/bin/vault-monitor.sh

Configure systemd timer for monitoring

Set up automated monitoring checks using systemd timers for regular secret management health verification.

[Unit]
Description=Vault Monitoring Check
After=vault.service
Requires=vault.service

[Service]
Type=oneshot
User=vault
ExecStart=/usr/local/bin/vault-monitor.sh
StandardOutput=journal
StandardError=journal
[Unit]
Description=Run Vault monitoring every 15 minutes
Requires=vault-monitor.service

[Timer]
OnCalendar=*:0/15
Persistent=true

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now vault-monitor.timer

Verify your setup

Test the complete secrets integration pipeline to ensure containers receive secrets from Vault correctly.

vault status
sudo systemctl status vault
vault kv get secret/app/database
podman logs myapp
cat /tmp/database-config.json
ls -la /tmp/vault-token-via-agent
/usr/local/bin/vault-monitor.sh
sudo systemctl status vault-monitor.timer
sudo journalctl -u vault-monitor.service -n 20

Common issues

SymptomCauseFix
Vault agent fails to startInvalid token or permissionsCheck token with vault token lookup and verify policy assignment
Templates not renderingSecret path doesn't existVerify secret exists with vault kv get secret/app/database
Container can't read secretsFile permissions or mount issuesCheck file ownership ls -la /tmp/*.json and mount syntax
Vault sealed after restartAuto-unseal not configuredRun vault operator unseal [KEY] or configure auto-unseal
Audit log not writingDirectory permissions issueVerify chown vault:vault /var/log/vault and log rotation setup
Token expiredTTL reached without renewalCreate new token with vault token create -policy=podman-secrets

Next steps

Running this in production?

Need this managed? Running this at scale adds a second layer of work: capacity planning, failover drills, cost control, and on-call. See how we run infrastructure like this for European teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle managed devops services for businesses that depend on uptime. From initial setup to ongoing operations.