The Definitive Guide to dbt Environment Variables & Secret Management (2025 Best Practices)

18 minutes to read
Get free consultation

 

Every analytics engineer has felt that jolt of panic. The moment you almost commit a file to Git only to realize it contains a production database password or a sensitive API key is a digital near-miss that highlights a critical vulnerability in modern data workflows. In the world of dbt (Data Build Tool), where profiles.yml files and project configurations are central to everything we do, managing these secrets is not just a development task; it is a core security requirement.

This is where dbt environment variables play a crucial role. They provide the fundamental mechanism for separating your configuration—the secrets, keys, and environment-specific settings—from your code. This separation forms the bedrock of a secure, scalable, and collaborative analytics engineering practice.

This guide offers a comprehensive, actionable framework for managing dbt secrets securely throughout the entire development lifecycle. From basic local setups to enterprise-grade, automated CI/CD pipelines, mastering this process is essential. It helps prevent catastrophic data breaches, ensures compliance with standards like SOC2 and GDPR, and empowers your team to collaborate effectively without putting your data at risk.

Why Hardcoding Secrets in dbt is a Ticking Time Bomb

Hardcoding credentials directly into your profiles.yml or any other project file is like leaving your house keys under the welcome mat. Though it might seem convenient initially, it exposes you to enormous and unnecessary risk. Once a secret is committed to a Git repository, you should consider it compromised. Even if you remove it later, it remains in the commit history, accessible to anyone with access to the repository.

The risks span multiple dimensions:

Separating configuration from code is such a vital principle that it is a central tenet of The Twelve-Factor App methodology, which offers best practices for building modern, scalable applications.

The Role of Environment Variables in dbt

dbt provides a clean and simple way to follow this principle: the env_var() Jinja function. This function instructs dbt to look for a variable in the execution environment (the machine or container running the dbt command) instead of the file itself.

Instead of this:

# profiles.yml (INSECURE)
my_dbt_project:
  target: dev
  outputs:
    dev:
      type: snowflake
      account: my_snowflake_account
      user: my_user
      password: MyPassword123 # <-- Hardcoded secret!
      # ... other settings

Use env_var() to fetch the password securely from the environment:

# profiles.yml (SECURE)
my_dbt_project:
  target: dev
  outputs:
    dev:
      type: snowflake
      account: my_snowflake_account
      user: my_user
      password: "{{ env_var('DB_PASSWORD') }}" # <-- Securely loaded from the environment
      # ... other settings

Now, the profiles.yml file is safe to commit because the actual secret lives outside the project’s code, exactly where it should be.

Foundational Layer: Local Development with .env Files

For individual developers working on their local machines, .env files provide an effective first line of defense against accidental secret exposure. An .env file is a simple text file that stores key-value pairs, which can be loaded into the environment before a program runs.

This method allows each developer on a team to maintain their own credentials for their development environment without ever committing them to the shared repository.

Step-by-Step: Setting Up Your .env File

# .env
DB_ACCOUNT=your_snowflake_account
DB_USER=your_dev_user
DB_PASSWORD=your_dev_password
DB_WAREHOUSE=dev_warehouse
DB_DATABASE=dev_db
DB_SCHEMA=analytics
echo ".env" >> .gitignore

Verify .env is listed inside .gitignore.

Referencing Variables in profiles.yml

With the .env file in place and ignored by Git, structure your profiles.yml to read all sensitive or environment-specific values using env_var(). This makes your profile configuration completely portable and secure.

# profiles.yml - A secure, environment-driven configuration

my_dbt_project:
  target: dev
  outputs:
    dev:
      type: snowflake
      # Account identifier
      account: "{{ env_var('DB_ACCOUNT') }}"
      # User credentials
      user: "{{ env_var('DB_USER') }}"
      password: "{{ env_var('DB_PASSWORD') }}"
      # Connection settings
      role: transformer
      warehouse: "{{ env_var('DB_WAREHOUSE') }}"
      database: "{{ env_var('DB_DATABASE') }}"
      schema: "{{ env_var('DB_SCHEMA') }}"
      threads: 4
      client_session_keep_alive: False

This setup ensures a clean separation. The profiles.yml defines the structure of your connection, while the .env file provides the values for each developer’s environment.

Enterprise-Grade Security: Integrating a Cloud Secret Manager

While .env files work well for local development, they do not scale for production environments or large teams. For managing secrets in production jobs, service accounts, and multiple deployment environments (staging, QA, prod), a centralized, secure, and auditable solution is essential.

This is where dedicated cloud secret managers fit in. Tools like AWS Secrets Manager, Google Cloud Secret Manager, Azure Key Vault, or HashiCorp Vault offer several key benefits:

Example Workflow: Using AWS Secrets Manager with dbt

Integrating a secret manager with dbt follows a workflow pattern. You do not connect dbt directly to the secret manager. Instead, the environment running dbt fetches the secrets and sets them as environment variables before dbt executes.

A typical AWS Secrets Manager setup includes these steps:

# This script runs before 'dbt run'
echo "Fetching secrets from AWS Secrets Manager..."

# Retrieve the secret JSON string
SECRET_JSON=$(aws secretsmanager get-secret-value --secret-id dbt/prod/snowflake_creds --query SecretString --output text)

# Export the values from the JSON as environment variables
export DBT_USER=$(echo $SECRET_JSON | jq -r .user)
export DBT_PASSWORD=$(echo $SECRET_JSON | jq -r .password)
# ... export other secrets

echo "Secrets fetched and exported successfully."
dbt run --profiles-dir .

When dbt run executes, DBT_USER and DBT_PASSWORD variables are present in the environment. The env_var() function in profiles.yml reads them, and the connection succeeds without any secret being stored in project files or on disk.

This pattern is highly secure, scalable, and adaptable to other secret managers and cloud providers.

Automating Security: Secrets in Your CI/CD Pipeline

The last piece is managing secrets in an automated environment like a CI/CD pipeline (GitHub Actions, GitLab CI). The goal remains to inject secrets at runtime so dbt accesses them while never storing secrets in the repository.

All major CI/CD platforms provide secure mechanisms for storing secrets.

Secure Configuration for GitHub Actions

In GitHub, secrets are stored at the repository or organization level under Settings > Secrets and variables > Actions. These are encrypted and exposed only to workflows you permit.

Here is a sample GitHub Actions YAML snippet showing how to pass these secrets as environment variables to a dbt run step.

# .github/workflows/dbt_run.yml

name: Run dbt Production Models

on:
  push:
    branches:
      - main

jobs:
  run_dbt:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.10'

      - name: Install dependencies
        run: |
          pip install dbt-snowflake python-dotenv

      - name: Run dbt models
        # Inject secrets from GitHub Actions into the environment
        env:
          DB_ACCOUNT: ${{ secrets.DBT_PROD_ACCOUNT }}
          DB_USER: ${{ secrets.DBT_PROD_USER }}
          DB_PASSWORD: ${{ secrets.DBT_PROD_PASSWORD }}
          DB_WAREHOUSE: prod_warehouse
          DB_DATABASE: prod_db
          # ... other env vars
        run: |
          dbt deps
          dbt run --select state:modified+

In this workflow, the env block maps encrypted GitHub secrets (e.g., secrets.DBT_PROD_PASSWORD) to the environment variables expected by your profiles.yml (e.g., DB_PASSWORD). Secrets are never printed to logs and exist only during job execution.

The Ultimate Checklist: Do's and Don'ts of dbt Secret Management

To help you and your team implement these practices, here is a quick, scannable checklist. Experience with clients shows that following these rules can prevent the most common and dangerous security pitfalls.

Do’s

Don’ts

Secure Your Data Pipelines with Stellans

Implementing a robust secret management strategy is foundational for a secure and scalable data platform. This requires expertise in both dbt best practices and cloud security architecture. While the principles are straightforward, tailoring them to your organization’s tools, compliance needs, and team structure can be complex.

At Stellans, we specialize in building data platforms centered on security and operational excellence. Our Stellans Data Pipeline Security Consulting service goes beyond theory. We audit your current dbt setup, design secure, scalable secret management workflows, and implement tools and processes to protect your most valuable data assets. We help you move from simply using dbt to mastering it securely.

Ready to build a trustworthy data platform? Schedule a free consultation with our data security experts today.

Frequently Asked Questions

How do I securely store credentials in dbt projects?
The most secure way combines environment variables with a dedicated secret manager like AWS Secrets Manager or HashiCorp Vault. For local development, use .env files and ensure they are in .gitignore. Never hardcode credentials directly in your profiles.yml file.

What are the best practices for using environment variables in dbt?
Use {{ env_var(‘YOUR_VARIABLE’) }} in your profiles.yml for any sensitive or environment-specific value (password, user, account, warehouse). Store these values in .env files locally and inject them from secret managers or CI/CD platforms in production.

How can I prevent secrets from being committed to git in dbt?
Always add files containing secrets, like .env, to .gitignore. Additionally, implement pre-commit hooks and automated scanning tools like git-secrets to detect and block accidental commits with sensitive information before pushing to remote repos.

Article By:

https://stellans.io/wp-content/uploads/2024/06/IMG_5527-2-1.png
Vitaly Lilich

Co-founder & CEO, Stellans

Related Posts

    Get a Free Data Audit

    * You can attach up to 3 files, each up to 3MB, in doc, docx, pdf, ppt, or pptx format.