Skip to content

Instantly share code, notes, and snippets.

@siavashs
Last active April 22, 2026 10:48
Show Gist options
  • Select an option

  • Save siavashs/48622655c10b26e385613096330b5c40 to your computer and use it in GitHub Desktop.

Select an option

Save siavashs/48622655c10b26e385613096330b5c40 to your computer and use it in GitHub Desktop.
Alertmanager: External Receiver Files

External Receiver Files

Add support for loading receiver configurations from external YAML files, keeping backwards compatibility with inline receivers in the main config.

Design Decisions

  • New YAML field receiver_files (list of glob patterns) on Config struct
  • Mutually exclusive with inline receivers: error if both are set
  • Glob patterns supported (e.g. receivers/*.yml), consistent with templates
  • Relative paths resolved against config file directory (same as templates)
  • Each file contains a YAML list of Receiver objects (one or more per file)
  • On reload failure for a file: keep previous receivers from that file, log error
  • Receiver names must still be globally unique across all files

Example

alertmanager.yml:

receiver_files:
  - 'receivers/*.yml'
route:
  receiver: team-X
  ...

receivers/team-x.yml:

- name: 'team-X'
  webhook_configs:
    - url: 'http://example.com/hook'
- name: 'team-X-pager'
  pagerduty_configs:
    - routing_key: 'secret'

Implementation Steps

1. Add receiver_files field to Config struct

  • config/config.go: Add ReceiverFiles []string field with yaml tag receiver_files

2. Add LoadReceiverFile function

  • config/config.go: New function that reads a single file, parses []Receiver via yaml.UnmarshalStrict, and returns the receivers
  • Validates each receiver has a non-empty name

3. Add LoadReceiverFiles function

  • config/config.go: Expands globs, calls LoadReceiverFile per matched file
  • On per-file failure: logs error, keeps previously loaded receivers for that file (requires a cache parameter or uses a simple map)
  • Returns merged []Receiver list
  • Checks for duplicate names across files

4. Update Config.UnmarshalYAML validation

  • Error if both Receivers and ReceiverFiles are non-empty
  • When ReceiverFiles is set, skip inline receiver validation (it's empty)

5. Update LoadFile to load external receivers

  • After Load(), if ReceiverFiles is set, expand globs (relative to config dir), load receivers, merge into cfg.Receivers
  • Then the existing validation in UnmarshalYAML (global defaults, receiver-route checks) can be deferred or re-run after merging
  • Key insight: receiver validation that applies global defaults (lines 412-676 in config.go) runs inside UnmarshalYAML, but external receivers won't exist yet at that point. We need to split this:
    • UnmarshalYAML handles inline receivers normally
    • LoadFile loads external receivers, applies the same global-defaults logic, then runs checkReceiver validation

6. Update resolveFilepaths

  • Also resolve ReceiverFiles paths relative to config dir (same as templates)
  • HTTPConfig directory resolution for externally-loaded receivers already handled since they're merged into cfg.Receivers

7. Update Coordinator for reload with stale-receiver fallback

  • config/coordinator.go: Keep a lastReceiversByFile map[string][]Receiver to enable fallback on per-file errors
  • On reload, pass this map to LoadReceiverFiles; on success for a file, update the map; on failure, reuse previous entries

8. Add tests

  • config/config_test.go: Test mutual exclusivity error, glob loading, per-file error with fallback, duplicate name detection, global defaults applied to external receivers
  • Add test data files under config/testdata/receivers/

9. Update cli/check_config.go

  • amtool check-config should also validate external receiver files
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment