iToverDose/Software· 23 MAY 2026 · 12:04

Secure Kamal deployments with AWS Secrets Manager on budget VPS

Learn how to replace plaintext API keys in Kamal deployments with AWS Secrets Manager, ensuring SOC 2 compliance while keeping costs low on Hetzner VPS. A step-by-step guide with working commands.

DEV Community4 min read0 Comments

Deploying applications with Kamal simplifies containerized workflows, but storing secrets in plaintext files creates security and compliance risks. Many teams face this challenge, especially when working with sensitive API keys or database credentials. The solution involves using AWS Secrets Manager to centralize secret storage and securely inject them during deployment—without breaking the budget.

Why plaintext secrets in Kamal are risky

By default, Kamal stores secrets in a .kamal/secrets file on your local machine. While convenient for development, this approach fails security audits like SOC 2 and GDPR. Plaintext secrets are vulnerable to theft during file system access, backups, or device compromise. Managed secret stores like AWS Secrets Manager provide encryption at rest, access controls, and audit trails—essential for compliance.

Setting up a cost-effective Hetzner VPS for deployment

Hetzner’s CAX series offers affordable virtual private servers starting at around 4 euros per month. A CX22 instance with 2 vCPUs and 4GB RAM is sufficient for production workloads. After provisioning your server, install Docker and configure SSH access to enable Kamal deployments:

sudo apt update && sudo apt install -y docker.io
ssh-copy-id root@your-server-ip

Next, define your deployment configuration in config/deploy.yml. This file should include server details, SSL settings, and registry credentials for Docker Hub:

servers:
  web:
    hosts:
      - runtime.yourdomain.com

proxy:
  ssl: true
  hosts:
    - runtime.yourdomain.com

healthcheck:
  path: /health/ready

registry:
  server: docker.io
  username: your-docker-user
  password:
    - KAMAL_REGISTRY_PASSWORD

You’ll need a Docker Hub account and a personal access token for the KAMAL_REGISTRY_PASSWORD field.

Creating and managing secrets in AWS Secrets Manager

AWS Secrets Manager centralizes secret storage and access. To store your application secrets:

  1. Navigate to the AWS Secrets Manager console.
  2. Select Store a new secret and choose Other type of secret.
  3. Switch to the Plaintext tab and paste your JSON-formatted secrets:
{
  "DEEPGRAM_API_KEY": "your_deepgram_key",
  "ASSEMBLY_AI_API_KEY": "your_assemblyai_key",
  "REDIS_URL": "redis://:password@your-redis:6379",
  "KAMAL_REGISTRY_PASSWORD": "your_docker_token"
}
  1. Name the secret myapp/production/secrets.
  2. Store it in a region close to your server, such as eu-central-1 (Frankfurt), to reduce latency and ensure GDPR compliance.

Granting secure access from your local machine

Your deployment machine needs IAM permissions to read secrets during the Kamal deploy process. Create an IAM user named kamal-deploy with restricted access:

  • Disable console access and generate credentials for CLI use.
  • Create an IAM group named secrets-manager and attach the SecretsManagerReadWrite policy.
  • Add an inline policy to allow batch secret retrieval:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret",
        "secretsmanager:BatchGetSecretValue",
        "secretsmanager:ListSecrets"
      ],
      "Resource": "*"
    }
  ]
}

Assign the user to the group and allow up to 30 seconds for policy propagation before testing.

Configuring AWS CLI and testing access

Configure the AWS CLI with your IAM credentials and preferred region:

aws configure

Verify access by fetching a portion of your secret:

aws secretsmanager get-secret-value \
  --secret-id myapp/production/secrets \
  --query SecretString \
  --output text | head -c 50

Successful output confirms your setup is working correctly.

Extracting secrets for Kamal deployments

Kamal’s --from flag expects individual AWS secrets per key. To avoid managing dozens of secrets, use the AWS CLI with Python or jq to extract specific values from a single JSON blob. For example, use this approach in your .kamal/secrets file:

# Using Python to extract values
DEEPGRAM_API_KEY=$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['DEEPGRAM_API_KEY'])" \
  "$(aws secretsmanager get-secret-value --secret-id myapp/production/secrets --query SecretString --output text)")

ASSEMBLY_AI_API_KEY=$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['ASSEMBLY_AI_API_KEY'])" \
  "$(aws secretsmanager get-secret-value --secret-id myapp/production/secrets --query SecretString --output text)")

Alternatively, use jq for cleaner syntax:

DEEPGRAM_API_KEY=$(aws secretsmanager get-secret-value \
  --secret-id myapp/production/secrets \
  --query SecretString \
  --output text \
  | jq -r '.DEEPGRAM_API_KEY')

Each line runs in a separate subshell, ensuring no shared state between keys.

Deploying with Kamal and AWS Secrets Manager

With your secrets configured in AWS and accessible via the CLI, initiate a deployment:

kamal deploy

During deployment, Kamal fetches secrets from AWS Secrets Manager and injects them into your container environment—eliminating the need for plaintext files on disk or in version control.

Scaling to multiple environments

For production and staging, use separate AWS secrets for each environment. Define environment-specific .kamal/secrets files to pull secrets dynamically:

# .kamal/secrets (production)
DEEPGRAM_API_KEY=$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['DEEPGRAM_API_KEY'])" \
  "$(aws secretsmanager get-secret-value --secret-id myapp/production/secrets --query SecretString --output text)")

KAMAL_REGISTRY_PASSWORD=$(python3 -c "import json,sys; print(json.loads(sys.argv[1])['KAMAL_REGISTRY_PASSWORD'])" \
  "$(aws secretsmanager get-secret-id myapp/production/secrets --query SecretString --output text)")

Repeat the process for staging with myapp/staging/secrets. This ensures clean separation and compliance across environments.

As applications grow, the need for secure, scalable secret management becomes critical. By integrating Kamal deployments with AWS Secrets Manager on a cost-effective Hetzner VPS, teams can achieve SOC 2 and GDPR compliance without sacrificing performance or budget. This setup not only protects sensitive data but also streamlines deployment workflows for modern cloud-native applications.

AI summary

Learn how to store Kamal secrets securely in AWS Secrets Manager and deploy to an affordable Hetzner VPS while maintaining SOC 2 and GDPR compliance.

Comments

00
LEAVE A COMMENT
ID #1VG070

0 / 1200 CHARACTERS

Human check

8 + 8 = ?

Will appear after editor review

Moderation · Spam protection active

No approved comments yet. Be first.