Vector’s secrets management is surprisingly flexible, allowing you to inject sensitive values into your configuration without embedding them directly, but the real magic is how it uses a layered approach to resolve them, letting you override values at runtime without touching your static config.

Let’s see it in action. Imagine you have a vector.toml file:

[sources.my_source]
type = "http_source"
# Instead of "my-api-key" directly here:

api_key = "{{ env.MY_API_KEY }}"

listen_address = "0.0.0.0:8080"

[sinks.my_sink]
type = "file"
# And for file paths too:

path = "{{ file.log_file_path }}"

encoding = "json"

Here, {{ env.MY_API_KEY }} tells Vector to look for an environment variable named MY_API_KEY. {{ file.log_file_path }} tells it to look for a value named log_file_path within a file.

Now, how do we provide these secrets?

Environment Variables:

The most common method is using environment variables. When you start Vector, you can set them:

export MY_API_KEY="supersecretapikey123"
export VECTOR_SOURCES_MY_SOURCE_API_KEY="overrideapikey456" # Example of overriding from env
vector --config vector.toml

In this scenario, Vector will read MY_API_KEY from the environment. If you have a more specific environment variable like VECTOR_SOURCES_MY_SOURCE_API_KEY, Vector’s hierarchical environment variable resolution will take precedence. This allows you to override specific configuration values directly from the environment, which is incredibly useful for CI/CD pipelines or Kubernetes deployments. The naming convention VECTOR_<COMPONENT_TYPE>_<COMPONENT_NAME>_<SETTING_NAME> is key here.

File-Based Secrets:

For more complex or larger secrets, or when you don’t want to clutter your environment, you can use files. Create a file, say secrets.toml:

log_file_path = "/var/log/my_app/events.log"
db_password = "mypassword123!"

Then, tell Vector to load this file using the secrets_file option in your vector.toml:

# vector.toml
secrets_file = "secrets.toml"

[sources.my_source]
type = "http_source"

api_key = "{{ env.MY_API_KEY }}" # Still looking for env var if present

listen_address = "0.0.0.0:8080"

[sinks.my_sink]
type = "file"

path = "{{ file.log_file_path }}" # This will now be resolved from secrets.toml

encoding = "json"

[sinks.db_sink]
type = "postgres"
# You can also use file-based secrets for other settings

connection_string = "postgres://user:{{ file.db_password }}@host:port/dbname"

When Vector starts, it will read secrets.toml. Any value referenced using {{ file.<key> }} will be populated from this file. If an environment variable with the same name as the file key exists (e.g., LOG_FILE_PATH), it will take precedence, following the same hierarchical rule as env. variables. This layering means you can have a default in your secrets file and override it with an environment variable for specific deployments.

Environment Variables vs. Files:

  • Environment Variables: Best for single, short values, or when integrating with orchestrators like Kubernetes or Docker Compose, which manage environment variables. They are also the most straightforward for overriding specific settings at runtime.
  • Files: Ideal for longer strings, multiple secrets, or when you want to keep secrets out of the direct environment, perhaps by mounting a file as a volume in a container.

The Mental Model:

Vector’s secrets management isn’t just about where to get values; it’s about how it prioritizes them. When you have a placeholder like {{ env.MY_API_KEY }} or {{ file.log_file_path }}, Vector performs a lookup in this order:

  1. Specific Environment Variable: VECTOR_<COMPONENT_TYPE>_<COMPONENT_NAME>_<SETTING_NAME> (e.g., VECTOR_SOURCES_MY_SOURCE_API_KEY)
  2. Generic Environment Variable: VECTOR_<SETTING_NAME> (e.g., VECTOR_API_KEY)
  3. Environment Variable: The plain environment variable (e.g., MY_API_KEY)
  4. File Secret: If secrets_file is configured, it looks for <SETTING_NAME> in the specified file.
  5. Default Value: If one exists in the component’s configuration.

This cascading lookup is your most powerful tool. It allows a single configuration file to be used across development, staging, and production, with secrets and specific overrides managed externally via environment variables or mounted files.

One subtle but powerful aspect of {{ file.<key> }} resolution is that the key itself is case-sensitive and must match exactly within the secrets file. However, the value provided by the file can be overridden by environment variables, and those environment variables follow the same cascading lookup as {{ env.<key> }}. This means you can have a default secret in a file, and then selectively override it with an environment variable that has a more specific name.

The next step in managing secrets, especially in distributed systems, involves exploring Vector’s integration with external secret managers like HashiCorp Vault or AWS Secrets Manager, allowing for even more robust and centralized secret handling.

Want structured learning?

Take the full Vector course →