> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devhelm.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-Environment Deploys

> Deploy DevHelm configs to staging and production with environment-specific overrides in CI/CD

Structure your YAML configs and CI workflows to handle multiple environments with shared base definitions and per-environment overrides.

## File structure

### Separate files per environment

The simplest approach — one file per environment:

```
devhelm.staging.yml
devhelm.production.yml
```

Each file is self-contained. Deploy the right file based on the branch or CI environment.

### Base + environment files

Share common definitions in a base file and add environment-specific resources in separate files:

```
config/
  base.yml          # shared channels, policies, tags
  staging.yml       # staging-only monitors
  production.yml    # production-only monitors
```

Deploy with multiple `-f` flags:

```bash theme={null}
devhelm deploy -f config/base.yml -f config/staging.yml --yes
```

Sections from all files are concatenated. Resource names must be **disjoint across files** — defining the same monitor name in two files is a validation error, not an override. There is no per-resource override merging; to vary a monitor between environments, use environment variables (below) or separate self-contained files. The one exception is `defaults.monitors`, which shallow-merges across files (later files win per field).

### Using environment variables

Share a single file and vary behavior with environment variables:

```yaml theme={null}
monitors:
  - name: API Health
    type: HTTP
    config:
      url: ${API_URL}
      method: GET
    frequencySeconds: 300
```

Set different values in each CI environment:

```bash theme={null}
# Staging
API_URL=https://staging-api.example.com devhelm deploy -f devhelm.yml --yes

# Production
API_URL=https://api.example.com devhelm deploy -f devhelm.yml --yes
```

<Note>
  `${VAR}` interpolation only works in **string** fields (URLs, tokens, names). Numeric fields like `frequencySeconds` cannot be interpolated — the value would arrive as a string and fail schema validation. To vary frequencies per environment, use separate files per environment.
</Note>

## GitHub Actions

### Using environments

```yaml theme={null}
jobs:
  deploy:
    strategy:
      matrix:
        env: [staging, production]
    environment: ${{ matrix.env }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: devhelmhq/setup-devhelm@v1
        with:
          api-token: ${{ secrets.DEVHELM_API_TOKEN }}
      - run: devhelm deploy -f devhelm.${{ matrix.env }}.yml --yes
```

### Sequential with approval

```yaml theme={null}
jobs:
  deploy-staging:
    environment: staging
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: devhelmhq/setup-devhelm@v1
        with:
          api-token: ${{ secrets.DEVHELM_API_TOKEN }}
      - run: devhelm deploy -f devhelm.staging.yml --yes

  deploy-production:
    needs: deploy-staging
    environment: production
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: devhelmhq/setup-devhelm@v1
        with:
          api-token: ${{ secrets.DEVHELM_API_TOKEN }}
      - run: devhelm deploy -f devhelm.production.yml --yes
```

Configure the `production` environment in GitHub Settings to require manual approval.

## GitLab CI

```yaml theme={null}
.deploy-monitoring:
  image: node:20
  script:
    - npm install -g devhelm
    - devhelm validate devhelm.$CI_ENVIRONMENT_NAME.yml
    - devhelm deploy -f devhelm.$CI_ENVIRONMENT_NAME.yml --yes

deploy-staging:
  extends: .deploy-monitoring
  stage: deploy
  environment: staging
  only: [main]

deploy-production:
  extends: .deploy-monitoring
  stage: deploy
  environment: production
  when: manual
  only: [main]
```

## Terraform

Use Terraform workspaces or separate state files:

```bash theme={null}
terraform workspace select staging
terraform apply -var-file=staging.tfvars

terraform workspace select production
terraform apply -var-file=production.tfvars
```

Or use separate directories:

```
terraform/
  staging/
    main.tf
    terraform.tfvars
  production/
    main.tf
    terraform.tfvars
```

## Best practices

* **Separate API tokens** per environment for isolation
* **Deploy staging first**, then promote to production
* **Use GitHub Environments** or equivalent for approval gates on production
* **Pin CLI versions** for reproducible builds across environments
* **Keep environment differences minimal** — vary URLs and frequencies, not monitor structure

## Next steps

<CardGroup cols={2}>
  <Card title="GitHub Actions" icon="github" href="/mac/ci-cd/github-actions">
    Full setup-devhelm action reference.
  </Card>

  <Card title="Environments CLI" icon="terminal" href="/cli/commands/environments">
    Manage environments from the command line.
  </Card>
</CardGroup>
