Documentation

We are here to help you get from zero to one fast.

Get Started    Discussions

Docker

reading time 10 mins

This guide will show you 4 production-ready ways of using Doppler to supply app config and secrets to your Docker containers:

Option

Usecase

Dockerfile

For users who want to install the Doppler CLI in their Dockerfile.

Container Env Vars

For users who want to inject the secrets from Doppler on docker run through args.

Mounted .env File

For users who want to mount an auto-generated .env file created by Doppler.

High Availability

For users who want high availability built into their Docker images.

Prerequisites

  • You've run applications in Docker and ideally, have experience building Docker images.

Service Tokens

Accessing your secrets in production or CI/CD environments requires a Service Token to provide read-only access to a specific config. It's exposed to the CLI via the DOPPLER_TOKEN environment variable which should be provided by your CI/CD environment, e.g. GitHub Secret.

Option 1: Dockerfile

This method installs the Doppler CLI installed in your Docker image to inject secrets at container runtime. It's a great option because your container orchestrator, docker run or docker-compose up command needs only a single DOPPLER_TOKEN environment variable.

πŸ“˜

Embedding the Doppler CLI comes with the other significant benefit of multi-line secrets support without escaping and flattening newline characters like is required if supplying environment variables to Docker.

Installing the CLI is a single command and works for all Linux containers.

# Install the Doppler CLI
RUN (curl -Ls https://cli.doppler.com/install.sh || wget -qO- https://cli.doppler.com/install.sh) | sh

Your Dockerfile will then need to use doppler run in either the ENTRYPOINT or CMD to fetch your secrets at container runtime. As a general rule, CMD is the easiest way to get started, but we'll explore both options below.

CMD method

CMD ["doppler", "run", "--", "printenv"]
  • Doesn't require understanding the difference between ENTRYPOINT and CMD
  • Good as it works with an existing ENTRYPOINT without requiring changes
  • Easily bypass the Doppler CLI by overriding theCMD at container runtime

ENTRYPOINT method

ENTRYPOINT ["doppler", "run", "--"]
CMD ["your-command-here"]
  • Good as it ensures any command used to run the container will have Doppler injected environment variables
  • Requires knowledge of ENTRYPOINT vs. CMD
  • Requires integrating into an existing ENTRYPOINT command or script if defined
  • Bypassing the use of the Doppler CLI in your ENTRYPOINT requires either conditional logic to only use Doppler if the DOPPLER_TOKEN environment variable is set, or overriding the ENTRYPOINT when running the container

πŸ“˜

Need more guidance? Reach out via in-product support or in our Community Portal

Example

Let's see a full example of a Dockerfile using the CMD option.

FROM alpine

# Install the Doppler CLI
RUN (curl -Ls https://cli.doppler.com/install.sh || wget -qO- https://cli.doppler.com/install.sh) | sh

# Fetch and view secrets using "printenv". Testing purposes only!
# Replace "printenv" with the command used to start your app, e.g. "npm", "start"
CMD ["doppler", "run", "--", "printenv"]

Because the contents of your Dockerfile has changed, you'll need to re-build it before continuing. If following along with this example, you'll need to build the image:

docker build -t doppler-test .

Now run the container:

# `DOPPLER_TOKEN` (Service Token) provided by CI/CD environment
docker run --rm -it --init -e DOPPLER_TOKEN="$DOPPLER_TOKEN" doppler-test
doppler setup # Select the project and config

# Use local Doppler configuration, passing in CLI token, project, and config
docker run --rm -it --init \
   -e "DOPPLER_TOKEN=$(doppler configure get token --plain)" \
   -e "DOPPLER_PROJECT=$(doppler configure get project --plain)" \
   -e "DOPPLER_CONFIG=$(doppler configure get config --plain)" \
   doppler-test

You should see your secrets output amongst the other container environment variables.


Option 2: Container Env Vars

If you're unable to install the Doppler CLI in your Dockerfile, the CLI can supply environment variables to the container using the docker run --env-file flag combined with doppler secrets download.

This method requires a bash shell (for process substitution) and the Doppler CLI to be installed in the environment running the container, e.g. AWS EC2 instance. Using the alpine image as an example:

docker run --rm --env-file <(doppler secrets download --no-file --format docker) alpine printenv

You should now see your secrets output amongst the other container environment variables.

πŸ“˜

Docker does not support multi-line secrets using --env-file so Doppler's --format docker flag also flattens multi-line secrets (escaping newlines) which can then be converted back to their original form in your application code.


Option 3: Mounted .env File

This guide will show you how to use Doppler to create a .env file mounted inside the container.

The first step is to use doppler secrets download to save the secrets to the a .env file.

doppler secrets download --no-file --format env > .env

Then mount the .env file when running your container.

# Using cat for testing purposes
docker run --rm --init -it -v $(pwd)/.env:/.env alpine cat .env

πŸ“˜

You can also download your secrets in JSON for mounting into the container using doppler secrets download --no-file --format json > secrets.json

Example

To put together a complete example using multi-line secrets and Docker, we'll create a Python Flask app that uses the python-dotenv library and requires two multiline secrets; a TLS certificate and key in order to serve requests over HTTPS.

While the example here uses the Python Flask library, the same principles apply to other languages and frameworks.

First, we'll create a new Doppler project named py-https using the CLI.

doppler projects create py-https

Next, save the following JSON to a secrets.json file which we'll use to populate the secrets of our py-https project (this is a self-signed cert and key and is ok to share for the purposes of a tutorial).

{"CERT":"-----BEGIN CERTIFICATE-----\r\nMIIEQTCCAqmgAwIBAgIQLTUB0ZS4OxxOivJYHtisMDANBgkqhkiG9w0BAQsFADB5\r\nMR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExJzAlBgNVBAsMHnJiQHJi\r\nLWRldi5sb2NhbCAoUnlhbiBCbHVuZGVuKTEuMCwGA1UEAwwlbWtjZXJ0IHJiQHJi\r\nLWRldi5sb2NhbCAoUnlhbiBCbHVuZGVuKTAeFw0xOTA2MDEwMDAwMDBaFw0zMDA3\r\nMDkwNDI2MzNaMFIxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBjZXJ0aWZp\r\nY2F0ZTEnMCUGA1UECwwecmJAcmItZGV2LmxvY2FsIChSeWFuIEJsdW5kZW4pMIIB\r\nIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx8LwKpBjHxJYaiKECqBqQUMb\r\n/hLiCDkRHna5qjE5pyFS7xAq1Cri/jklRjTLMAGGv/l1WJwMMY9+sCfaHPrs0ocP\r\nMeposYZa1NnVoVIdsumBgqFTsbG1InAihkpW0NwbFaAIrMC83Iia7OUJC7mHE5YD\r\ncCH+haHtoUAIO6p/plkNa3ZqmmrSAkrDQ3FYsrapUKANHbaIqLoMT0hnyQuolOUS\r\ny+WxGCcJaL/RHeF7zcZaVug0ZNA9lK4nXOElymXdyr4bfa5TP0Wno2zMJ3gU09m1\r\n5LSQOBII2UR+MiJYVAtj/X3tlygjansTpR2MqKn8unQalSlBdvG8AYYAh4n61wID\r\nAQABo2wwajAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDAYD\r\nVR0TAQH/BAIwADAfBgNVHSMEGDAWgBQIz6S3Zbmim8eViQ9VABpTuamuBDAUBgNV\r\nHREEDTALgglkZXYubG9jYWwwDQYJKoZIhvcNAQELBQADggGBADGxdbxqpCCsgNsZ\r\nTpQz2hFultvrRovnGshn/zzpwt2/xTmp9sXqBpbVsDVe2pss8EpqmR0Zg2RPCi3o\r\nR35Gn2Rt1rcVP468EQfia7Vz0Mz9OknYQL7HChldJEuzWAnCGdMjntTWl69Z0/Ji\r\nbneY1O5s6cG9Y1G1ezjuVGBdywOEsS68wCSKpKgNCoF6z+f8SxdDR3E1Ubhyirhm\r\nBVvaeTxaBBQnbcjQXYm70oSB5B85+09zdQx7oCRckyFtL5v08Dj2y8tD6wsUsBe6\r\n9+iVFGXVvRVWDHtASQ1wGZo/WdR+zp38kZ5sPb2KOCVjo4uLxULD7QEu7mTtdTum\r\nTv9LNmSy3kZfkxx4a+nyb8VjZU6a/Z5C22B6uvBPUVqEQDlo+BXLzBlP5/o0Hmmm\r\n5ioIFlX+4GJhpbMjniFwVufA5nNWCrjAe05ggQB83szos1aMCMVIHwmVo5X7n/d3\r\n4eEOHPslR5p2zjynd+r3i54cu8ASmvl+v2vmh9r2VGJDyyrGLA==\r\n-----END CERTIFICATE-----","KEY":"-----BEGIN PRIVATE KEY-----\r\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDHwvAqkGMfElhq\r\nIoQKoGpBQxv+EuIIOREedrmqMTmnIVLvECrUKuL+OSVGNMswAYa/+XVYnAwxj36w\r\nJ9oc+uzShw8x6mixhlrU2dWhUh2y6YGCoVOxsbUicCKGSlbQ3BsVoAiswLzciJrs\r\n5QkLuYcTlgNwIf6Foe2hQAg7qn+mWQ1rdmqaatICSsNDcViytqlQoA0dtoiougxP\r\nSGfJC6iU5RLL5bEYJwlov9Ed4XvNxlpW6DRk0D2Uridc4SXKZd3Kvht9rlM/Raej\r\nbMwneBTT2bXktJA4EgjZRH4yIlhUC2P9fe2XKCNqexOlHYyoqfy6dBqVKUF28bwB\r\nhgCHifrXAgMBAAECggEBAI751KHyZC+qFsQoXi+o70Q0v0VTyJowFjOisHLC9svL\r\niyt2e+ENrRheWHJuePusLAEdkELStduHJBr6+x8A7h4k8tg8OaDOVBbCryfeuUkD\r\n3mFcDKZDVi0G638ImCi3UV9iArN64/JIh/KEX7wXlzmKhn1sp1qIMqfWR85E00aA\r\nc2u1WL3iUSZO+61L8czab6MFzE6koK220O9s3TP54JnM2WcgWQ+/nu0kSv09XRXz\r\nZ4jLJOwFnqemUPxKCvNUCXYS9Nn4Z2qirC1KE2/4n1XOjhJu/5zBCGgtDikhFhtK\r\nC3sfqOt6sThpeo4OuWO83slhyD6CiZpxkK5tMKLeVYkCgYEAzYhBv/XBFAkMIikx\r\nwMyfBB/O8uCaMGW8BoAToCduyYLC/8quz0doKCXxDLZuO33G0w0M7cHebxNvth7b\r\nqCFdBKooYMiU8Wur8t5UK+hEq1fxQVu8a3Vud0RF0jGxL58jRnqwDD7i5KXgaNXs\r\ntvkqHoRlyHXPThErgfxc/5IbdBMCgYEA+M/uRDVHRpcqTiU2/dvipjQLmzMXRdy6\r\n+ld2Pu97QqFZZDVoJwBAGJSgfzRFtKlY8XnhOezBeGuow5VWajqrv3s7xDs5Uspn\r\nY1Tp56YEMO92aei9MLVXN0NO6zBbxPS3WlGWNPtcv04T1lNLzF3sVy7gRukzwgA3\r\n3qYgpzw+jq0CgYEAtGY7Z7TaEq7aQoWr0NEJZcJRj+bKD17MSVxTXYge+qpWY9PP\r\n2cmQR2T+Da12VatRP1++EFXQ2efSbqTpIcqe7YA4zrJf4QMjupmggaVt2ILpE/fk\r\nCuHY4sA/FH5fSjBA8xSuvyDXX9+keAAEtJlAeea1u9yD5760VHRgYEYq6JcCgYEA\r\n8VohsiBMZ3kzHGKVGWM2VCqgJ4hDxwz6guRcW2hKlLOW1tHHmi1v/2gM19eI1lp/\r\nYJ3tkBbNBVbJeg9ep+UJvNB5hTw9usWDzKMN0hsEbcObhdixzJHbbEvqPdHG8yK+\r\nyOnjXrmFmjzOmVRrbm6dV2StQZvtDP7RMzTK7+5McHECgYA1Z9n4js5r1w3kLpjo\r\nmsEqxoWoZt+5ENfkCUNOMVtv0qZhoMqTUMkysqKZ6iMK1y6NHWSTNXO5Ad7Jpgkm\r\nQayYHCah5y5Cf/ch6CqxUuJyjVD6LwO+qHB+HIrsHAmY1KjqcrupC9konAKuu0S9\r\n2whnj2PSxugfGj8XDb4UhejZ7A==\r\n-----END PRIVATE KEY-----"}

Now let's upload the secrets, then delete the secrets.json file

doppler secrets upload --project py-https --config dev secrets.json && rm secrets.json

Next, let's build the Docker image which we'll do in two stages.

First, save the following to app.py which our Dockerfile will copy.

from os import environ as env
from dotenv import load_dotenv
from flask import Flask

app = Flask(__name__)

load_dotenv()
open('cert.pem', 'w').write(env['CERT'])
open('key.pem', 'w').write(env['KEY'])


@app.route('/')
def hello():
    return 'Flask HTTPS wth Doppler multiline secrets'


app.run(host='0.0.0.0', port='8443', ssl_context=('cert.pem', 'key.pem'))

Save this Dockerfile locally.

FROM python:alpine

RUN pip install --quiet --upgrade pip python-dotenv flask
WORKDIR /usr/src/app
COPY app.py .
CMD ["python", "-u", "app.py"]

Then build the Docker image.

docker build -t py-https .

Now we're ready to emulate a deploy script that would generate the .env file and run the container.

#!/usr/bin/env bash

# Generate .env file
doppler secrets download --project py-https --config dev --no-file --format env > .env

# Run container and mount .env file
docker run --rm --init --name py-https -p 8443:8443 -v $(pwd)/.env:/usr/src/app/.env py-https

You should now be able to access the server at https://localhost:8443/.

To clean up all resources created for this example, stop the container by pressing CTRL+C, then run the following.

rm -f .env secrets.json app.py Dockerfile
docker image rm py-https
doppler projects delete https -y

Option 4: High Availability

In the rare event that Doppler is down, you can optionally add high availability to your Docker images by creating an encrypted snapshot of the secrets at build time. This also allows images to be built for specific environments that do not require network access to the Doppler API as the Doppler CLI will fallback to the saved encrypted snapshot.

Please note that if you intend to use Doppler without network access during runtime, the DOPPLER_TOKEN will still need to be provided as it is used as the decryption key for the encrypted snapshot.

🚧

Using high availability will embed a snapshot of your config's secrets in the image. This image is now dedicated to that config and should not be reused across environments.

Let's see a full example of a Dockerfile with high availability:

FROM alpine

# Install the Doppler CLI
RUN (curl -Ls https://cli.doppler.com/install.sh || wget -qO- https://cli.doppler.com/install.sh) | sh

# Pass `DOPPLER_TOKEN` at build time to create an encrypted snapshot for high-availability
ARG DOPPLER_TOKEN

# Create encrypted snapshot for high availability
RUN doppler secrets download doppler.encrypted.json

# Fetch secrets and print them using "printenv" command
ENTRYPOINT ["doppler", "run", "--fallback=doppler.encrypted.json", "--"]
CMD ["your-command-here"]
FROM alpine

# Install the Doppler CLI
RUN (curl -Ls https://cli.doppler.com/install.sh || wget -qO- https://cli.doppler.com/install.sh) | sh

# Pass `DOPPLER_TOKEN` at build time to create an encrypted snapshot for high-availability
ARG DOPPLER_TOKEN

# Create encrypted snapshot for high availability
RUN doppler secrets download doppler.encrypted.json

# Fetch secrets and print them using "printenv" command
CMD ["doppler", "run", "--fallback=doppler.encrypted.json", "--", "your-command-here"]

🚧

High RPS?

If you are deploying this image to serverless infrastructure like Lambda or CloudRun that results in high RPS (+120 req/min) to Doppler's API, we recommend setting the --fallback-only flag on the doppler run command in the ENTRYPOINT.

# Read secrets from the snapshot and print them using "printenv" command
# Fetch secrets and print them using "printenv" command
ENTRYPOINT ["doppler", "run", "--fallback=doppler.encrypted.json", "--fallback-only", "--"]
CMD ["your-command-here"]
# Read secrets from the snapshot and print them using "printenv" command
CMD ["doppler", "run", "--fallback=doppler.encrypted.json", "--fallback-only", "--", "your-command-here"]

The DOPPLER_TOKEN is then passed in as a build-arg when building the image:

docker build --build-arg "DOPPLER_TOKEN=$DOPPLER_TOKEN" -t doppler-ha .

πŸ‘

Amazing Work!

Now you know 4 methods for using Doppler for managing app secrets and configuration in Docker.

Updated 24 days ago


Docker


reading time 10 mins

Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.