Skip to content

Configs and secrets

Sometimes you can't use environment variables for configuration. Service configs allow you to create configuration files and attach them to your services.

Secrets work in a similar way, but are additionally encrypted during transit and at rest. You can use these for things like passwords or private keys.

Usage

Heads up!

Right now you can add a config or secret only when creating a new project. If you might want to change some things in future, use a templated config and add some variables.

In future versions we'll add a way to modify configs of already running projects (it is a bit tricky).

  1. Click the + Add a config file next to the Environment variables tab.

  2. Choose if you want to add a config or secret and pick a name for it.

  3. Fill out the contents of your config or secret.

  4. In your service, add a reference to the config or secret:

    services:
      app:
        ...
        configs:
          - { source: config_name, target: /path/to/config/file }
        secrets:
          # will be saved in /run/secrets/{secret_name}
          - secret_name
    

Templating

Configs and secrets can be used with templates, allowing you to dynamically customize configuration files. You can inject metadata about the running task, environment variables, as well as other secrets and configs.

Here are some possible things you can do:

{{ env "VAR" }}

Add the value of an environment variable to the file. The variable must be exposed on the service for this to work. For example, to add the ${DOMAIN} value to a file, add the following lines to the service:

services:
  app:
    environment:
      - DOMAIN
    configs:
      - { source: config_json, target: /app/config.json }

Then, in your config:

{
  "app_domain": "{{ env "DOMAIN" }}"
}

{{ secret "secret_name" }}

Add the value of a secret. This secret should be accessible to your service. For example, to read Redis password from a secret into a config, add the following lines to the service:

services:
  redis:
    configs:
      - { source: redis_conf, target: /usr/local/etc/redis/redis.conf }
    secrets:
      - redis_pwd

Then, in your config:

port 1337
requirepass {{ secret "redis_pwd" }}

This allows you to manage configuration file template separatly from the actual credential.

Task metadata

You can also expand information about the running task and service. This might be useful if you run multiple instances of a service and need a way to quickly distinguish them in a config file.

Here's the list of available variables:

  • {{ .Service.ID }}
  • {{ .Service.Name }}
  • {{ .Service.Labels }}
  • {{ .Task.ID }}
  • {{ .Task.Name }}
  • {{ .Task.Slot }}
  • {{ .Node.ID }}
  • {{ .Node.Hostname }}
  • {{ .Node.Platform.Architecture }}
  • {{ .Node.Platform.OS }}

Under the hood

You may have noticed that adding a config also defines is as external in your Compose file. That's because you can't define config contents in the Compose file, but can either read it from a file or use an externally managed one. Because Lunni doesn't have a filesystem, we are only left with option two.

Here's how it works:

  1. When you click Create project, Lunni first creates your configs with name prefixed with a project name.

  2. It then deploys a stack for the project using your Compose file, where the config is defined like this:

    configs:
      config-name:
        external: true
        name: ${PROJECT_NAME?}_config-name
    
  3. Docker finds the config by name and attaches it to the stack.