Build Secrets

You can set secrets for your applications, but these are only available at run-time. They aren’t available when building your Docker image without a little extra work.

To make a secret available at build time, we’ll use Docker secrets.

It’s a 2-step process:

  1. Mount a secret into your Dockerfile
  2. Provide the value for that secret when running fly deploy

Mounting secrets

Mount a secret into your Dockerfile within a RUN statement:

# Note: You can mount multiple secrets
RUN --mount=type=secret,id=MY_SUPER_SECRET \
    MY_SUPER_SECRET="$(cat /run/secrets/MY_SUPER_SECRET)" some_command \
    && more_commands_maybe

This creates a new file when running docker build. Secrets are stored in the /run/secrets directory. The file name is the id you passed when mounting the secret. The content of that file contains the value of the secret.

The --mount directive is not a shell command, so there’s no need to add && after it as you commonly see when chaining commands.

Secret values

You need to provide the values of the mounted secrets when running fly deploy:

# Note: You can pass multiple secrets if you need
fly deploy \
    --build-secret MY_SUPER_SECRET=some_value

Testing build secrets locally

If you want to test your Docker build locally (before deploying to Fly.io), the commands to do so would look something like this:

echo -n "secret_value" > mysecret.txt

docker build --secret id=MY_SUPER_SECRET,src=mysecret.txt .

Automate the inclusion of build secrets using an ephemeral Machine

The above requires you to have access to the values of secrets and to provide those values on the command line. If this poses a problem, an alternative may be to create an ephemeral Machine using the fly console command to do the deployment.

An example Dockerfile you can use for that purpose:

# syntax = docker/dockerfile:1

FROM flyio/flyctl:latest as flyio
FROM debian:bullseye-slim

RUN apt-get update; apt-get install -y ca-certificates jq

COPY <<"EOF" /srv/deploy.sh
#!/bin/bash
deploy=(flyctl deploy)
touch /srv/.secrets

while read -r secret; do
  echo "export ${secret}=${!secret}" >> /srv/.secrets
  deploy+=(--build-secret "${secret}=${!secret}")
done < <(flyctl secrets list --json | jq -r ".[].Name")

deploy+=(--build-secret "ALL_SECRETS=$(base64 --wrap=0 /srv/.secrets)")
${deploy[@]}
EOF

RUN chmod +x /srv/deploy.sh

COPY --from=flyio /flyctl /usr/bin

WORKDIR /build
COPY . .

The deploy.sh script contained within this Dockerfile will provide each secret individually as well as package up a script to set all secrets at once. You can set all build secrets at once in your Dockerfile using:

RUN --mount=type=secret,id=ALL_SECRETS \
    eval "$(base64 -d /run/secrets/ALL_SECRETS)" && \
    some_command

Assuming your builder Dockerfile is named Dockerfile.builder, you can launch the emphemeral machine using the following command:

flyctl console --dockerfile Dockerfile.builder -C "/srv/deploy.sh" --env=FLY_API_TOKEN=$(fly auth token)