kamal envify

Creates .env by evaluating .env.erb (or .env.staging.erb -> .env.staging when using -d staging) and push env files to the servers containing runtime secrets.

You can use this to manage secrets externally from your repository. .env is used for both build and runtime secrets.

If you want to manage the .env file yourself, you can instead push the runtime secrets with kamal env push (see kamal env).

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