Environment Variables

Using .env file to load required environment variables

Kamal uses dotenv to automatically load environment variables set in the .env file present in the application root. This file can be used to set variables like KAMAL_REGISTRY_PASSWORD or database passwords. But for this reason you must ensure that .env files are not checked into Git or included in your Dockerfile! The format is just key-value like:

KAMAL_REGISTRY_PASSWORD=pw
DB_PASSWORD=secret123

Using a generated .env file

1Password as a secret store

If you’re using a centralized secret store, like 1Password, you can create .env.erb as a template which looks up the secrets. Example of a .env.erb file:

<% if (session_token = `op signin --account my-one-password-account --raw`.strip) != "" %># Generated by kamal envify
GITHUB_TOKEN=<%= `gh config get -h github.com oauth_token`.strip %>
KAMAL_REGISTRY_PASSWORD=<%= `op read "op://Vault/Docker Hub/password" -n --session  #{session_token}` %>
RAILS_MASTER_KEY=<%= `op read "op://Vault/My App/RAILS_MASTER_SECRET" -n --session #{session_token}` %>
MYSQL_ROOT_PASSWORD=<%= `op read "op://Vault/My App/MYSQL_ROOT_PASSWORD" -n --session #{session_token}` %>
<% else raise ArgumentError, "Session token missing" end %>

This template can safely be checked into git. Then everyone deploying the app can run kamal envify when they setup the app for the first time or passwords change to get the correct .env file.

If you need separate env variables for different destinations, you can set them with .env.destination.erb for the template, which will generate .env.staging when run with kamal envify -d staging.

Note: If you utilize biometrics with 1Password you can remove the session_token related parts in the example and just call op read op://Vault/Docker Hub/password -n.

Bitwarden as a secret store

If you are using open source secret store like bitwarden, you can create .env.erb as a template which looks up the secrets.

You can store SOME_SECRET in a secure note in bitwarden vault:

$ bw list items --search SOME_SECRET | jq
? Master password: [hidden]

[
  {
    "object": "item",
    "id": "123123123-1232-4224-222f-234234234234",
    "organizationId": null,
    "folderId": null,
    "type": 2,
    "reprompt": 0,
    "name": "SOME_SECRET",
    "notes": "yyy",
    "favorite": false,
    "secureNote": {
      "type": 0
    },
    "collectionIds": [],
    "revisionDate": "2023-02-28T23:54:47.868Z",
    "creationDate": "2022-11-07T03:16:05.828Z",
    "deletedDate": null
  }
]

… and extract the id of SOME_SECRET from the json above and use in the erb below.

Example .env.erb file:

<% if (session_token=`bw unlock --raw`.strip) != "" %># Generated by kamal envify
SOME_SECRET=<%= `bw get notes 123123123-1232-4224-222f-234234234234 --session #{session_token}` %>
<% else raise ArgumentError, "session_token token missing" end %>

Then everyone deploying the app can run kamal envify and kamal will generate .env

Using env variables

You can inject env variables into the app containers using env:

env:
  DATABASE_URL: mysql2://db1/hey_production/
  REDIS_URL: redis://redis1:6379/1

Note: Before you can start the containers you need to push the env variables up to the servers.

Using secret env variables

If you have env variables that are secret, you can divide the env block into clear and secret:

env:
  clear:
    DATABASE_URL: mysql2://db1/hey_production/
    REDIS_URL: redis://redis1:6379/1
  secret:
    - DATABASE_PASSWORD
    - REDIS_PASSWORD

The list of secret env variables will be expanded at run time from your local machine. So a reference to a secret DATABASE_PASSWORD will look for ENV["DATABASE_PASSWORD"] on the machine running Kamal. Just like with build secrets.

If the referenced secret ENVs are missing, the configuration will be halted with a KeyError exception.

Note: Marking an ENV as secret currently only redacts its value in the output for Kamal. The ENV is still injected in the clear into the container at runtime.

Injecting env variables from the host

You can inject variables from the deploy host with the ${} syntax:

env:
  DATACENTER: "${DATACENTER}"

Using Kamal env variables

The following env variables are set when your container runs:

KAMAL_CONTAINER_NAME: this contains the current container name and version