Compose: Secrets management
Zulip’s Docker container uses Docker secrets to synchronize secrets between services, as well as within Zulip itself. These secrets are used to authenticate connections between services in the deployment, serving as defense in depth from SSRF, as well as to authenticate to outside providers (e.g. outgoing email services).
Docker Compose offers two backends for secrets – from the environment, or from files on disk.
Store secrets in a .env file
The simplest deployment technique is to provide secrets in the environment, most
commonly stored in an environment file. Note that is the environment for the
compose file itself, not the environment of the container. Environment
variables defined in .env do not directly propagate into the container’s
environments.
Place in compose.override.yaml (alongside any other configuration):
secrets:
zulip__postgres_password:
environment: "ZULIP__POSTGRES_PASSWORD"
zulip__memcached_password:
environment: "ZULIP__MEMCACHED_PASSWORD"
zulip__rabbitmq_password:
environment: "ZULIP__RABBITMQ_PASSWORD"
zulip__redis_password:
environment: "ZULIP__REDIS_PASSWORD"
zulip__secret_key:
environment: "ZULIP__SECRET_KEY"
zulip__email_password:
environment: "ZULIP__EMAIL_PASSWORD"
In a file named .env (which should not be checked into version control),
provide the secrets.
ZULIP__POSTGRES_PASSWORD=example_postgres_password
ZULIP__MEMCACHED_PASSWORD=example_memcached_password
ZULIP__RABBITMQ_PASSWORD=example_rabbitmq_password
ZULIP__REDIS_PASSWORD=example_redis_password
ZULIP__SECRET_KEY=example_django_secret_key
ZULIP__EMAIL_PASSWORD=example_outgoing_email_password
See the .env file syntax for a complete reference on the syntax.
Store secrets in files in a directory
Secrets can also be stored as flat files on disk, at paths of your choosing.
Place in compose.override.yaml (alongside any other configuration):
secrets:
zulip__postgres_password:
file: /path/to/secrets/postgres
zulip__memcached_password:
file: /path/to/secrets/memcached
zulip__rabbitmq_password:
file: /path/to/secrets/rabbitmq
zulip__redis_password:
file: /path/to/secrets/redis
zulip__secret_key:
file: /path/to/secrets/django_key
zulip__email_password:
file: /path/to/secrets/outgoing_email
Then, in /path/to/secrets/, place each of those files. Take care to not
include trailing newlines in them; for example:
echo -n "example_postgres_password" > /path/to/secrets/postgres
Additional secrets
If your deployment needs additional secrets, you must prefix them with
zulip__, and add them to the top-level secrets element, as well as the
secrets attribute for the zulip service.
For instance, to add a giphy_api_key secret, the compose.override.yaml might
contain:
secrets:
# Standard secrets
zulip__postgres_password:
environment: "ZULIP__POSTGRES_PASSWORD"
zulip__memcached_password:
environment: "ZULIP__MEMCACHED_PASSWORD"
zulip__rabbitmq_password:
environment: "ZULIP__RABBITMQ_PASSWORD"
zulip__redis_password:
environment: "ZULIP__REDIS_PASSWORD"
zulip__secret_key:
environment: "ZULIP__SECRET_KEY"
zulip__email_password:
environment: "ZULIP__EMAIL_PASSWORD"
# New, additional secret
zulip__giphy_api_key:
environment: "ZULIP__GIPHY_API_KEY"
services:
zulip:
# Tell Docker Compose that the zulip container needs access to the additional secret
secrets:
- zulip__giphy_api_key
With an additional value in .env:
# Standard secrets
ZULIP__POSTGRES_PASSWORD=...
ZULIP__MEMCACHED_PASSWORD=...
ZULIP__RABBITMQ_PASSWORD=...
ZULIP__REDIS_PASSWORD=...
ZULIP__SECRET_KEY=...
ZULIP__EMAIL_PASSWORD=...
# Additional secret
ZULIP__GIPHY_API_KEY=PS42beKkLnOUBOqb1BgTyna87ooKgthE
Rotating the PostgreSQL password
The zulip__postgres_password secret is used by the database
container only at first boot, when it creates the zulip PostgreSQL
user with that password. Changing ZULIP__POSTGRES_PASSWORD (or
swapping its source file) afterwards does not propagate to the
already-initialized database; the zulip container will then fail
to authenticate.
To rotate the password on a running deployment, update both sides:
Run an
ALTER ROLEquery against the database with the new password:docker compose exec database \ psql -U zulip -c "ALTER ROLE zulip WITH PASSWORD 'new_password';"
Update
ZULIP__POSTGRES_PASSWORDin your.envfile (or the file-based secrets backend you’ve configured) to match.Restart the
zulipcontainer so it picks up the new secret:docker compose up -d zulip