Configuration Management and Desired State Questions
Managing server and system configuration at scale with tools like Ansible, Puppet, Chef, and Salt. Covers the desired-state model, idempotency, convergence, inventory and role organization, secrets handling, and how configuration management differs from and complements provisioning. Keeping fleets consistent and reproducible over time.
Create an Ansible Jinja2 template for /etc/myapp/config that consumes variables: environment, db_host, log_level and includes placeholders for secrets to be retrieved at runtime. Explain how you'd provide variables securely in CI, and how you'd test rendering locally without exposing secrets.
Sample Answer
Approach: create a defensive Jinja2 template that uses required variables and inserts placeholders (lookups) for secrets retrieved at runtime. Show an Ansible task that renders the template while retrieving secrets from a secret backend (examples: HashiCorp Vault or Ansible Vault). Explain CI secrets best-practices and safe local testing commands.
Template (templates/myapp.config.j2):
# /etc/myapp/config - generated by Ansible
environment = {{ environment | default('production') }}
db_host = {{ db_host | default('127.0.0.1') }}
log_level = {{ log_level | default('INFO') }}
# Secrets are resolved at runtime via lookups (placeholder), not stored in repo
db_password = {{ lookup('hashi_vault', 'secret/data/myapp db_password field=value', wantlist=False) }}
api_key = {{ lookup('env', 'MYAPP_API_KEY') }}
# If using Ansible Vault instead:
# db_password = {{ lookup('ansible.builtin.vault', 'myapp_db_password') }}
Ansible task to render (playbook snippet):
- name: Render myapp config
hosts: myhosts
vars:
environment: "{{ environment }}"
db_host: "{{ db_host }}"
log_level: "{{ log_level }}"
tasks:
- name: Template config with runtime secret lookups
template:
src: templates/myapp.config.j2
dest: /etc/myapp/config
owner: root
mode: '0600'
How to provide variables securely in CI:
- Use the CI provider's secret store (GitHub Actions Secrets, GitLab CI variables, CircleCI context) to inject non-checked-in values at runtime; these are masked in logs.
- Prefer dynamic secret backends (HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager) with short-lived tokens. CI jobs authenticate with an ephemeral role (OIDC) and request secrets at runtime; do not embed static credentials.
- For Ansible-specific secrets, use Ansible Vault for repo-encrypted values or integrate Ansible with Vault lookup plugin so playbooks pull secrets live.
- Ensure least privilege and audit logs for secret access.
How to test rendering locally without exposing secrets:
- Use dummy/test secrets locally: create a local .env or vars file excluded from VCS and pass with --extra-vars "@local_test_vars.yml".
- Use jinja2-cli to render template with safe test values:
pip install jinja2-cli && jinja2 templates/myapp.config.j2 test_vars.yml -o ./out.config - Or run ansible-playbook against localhost with mocked lookups by setting environment variables or using a local Vault dev server. Example:
ANSIBLE_VAULT_PASSWORD_FILE=/tmp/vault_pass ansible-playbook playbook.yml --check --limit localhost - Always avoid printing secrets in CI logs: run with set -o pipefail and ensure tasks that fetch secrets don't use debug or register + dump.
- Validate permissions: ensure rendered file mode is 0600 and owner root, and run a syntax check: ansible-playbook --syntax-check and ansible-playbook --check --diff.
Reasoning summary:
- Keep secret retrieval out of repo by using lookup plugins at runtime.
- Prefer secret managers with short-lived creds for CI.
- Local testing should use mocked/dummy secrets or ephemeral dev secret backends and never commit real secrets.
Design a drift-detection system for a fleet of 50,000 Linux VMs across multiple regions. Requirements: detect near-real-time drift for critical settings (SSH configuration, important package versions), minimize false positives, scale efficiently, and integrate with automated remediation and alerting pipelines.
Sample Answer
Requirements & constraints (clarify): detect near‑real‑time drift for critical settings (SSH config, package versions) across 50k Linux VMs in multiple regions; low false positives; scalable; integrates with automated remediation and alerting.
High-level architecture:
- Lightweight agent on each VM that computes local canonical facts (SSHD config hash, allowed keys checksum, package versions for watched packages) and change events.
- Regional collectors (stateless) receive events over mTLS, validate/sign, and push to an event bus (Kafka).
- Stream processor (Flink/Beam) applies rules, enrichment, anomaly scoring, de‑duplication, and grouping.
- Store: immutable event log + indexed configuration store (Elasticsearch or Postgres + caching) for history.
- Orchestrator: remediation service with runbook library, limits (rate, circuit breakers), and webhook/alert integration (PagerDuty, Slack).
Core components & responsibilities:
- Agent: run periodic checks + detect FS/config diffs (inotify + periodic reconcile), send diffs + context (host metadata, baseline ID).
- Collectors: buffering, auth, region affinity, backpressure.
- Stream processor: apply deterministic rules (explicit policy mismatches), anomaly detection (score based on baseline drift frequency, risk), and suppress noise (aggregate per host/policy, windowing).
- Remediation orchestrator: verify drift (run pre-checks), apply safe automated remediation (idempotent scripts, config management push), and create incident if remediation fails.
- UI/Alerting: prioritized alerts with risk score, root-cause links, suggested remediation.
Minimizing false positives:
- Use baselines per host-class and allowlist known intentional deviations.
- Two‑step confirmation: local agent flags + reconfirmation from collector or agent-run “verify” before firing high‑priority alert/remediation.
- Rate limiting, anomaly scoring, and grouping (N hosts in M minutes) to avoid noisy alerts.
- Canary remediation: test on small cohort, verify success, then roll out.
Scalability & reliability:
- Agents push compact diffs and leverage local caching to avoid floods.
- Kafka partitions by region/host to scale to 50k QPS peaks.
- Autoscaling collectors and stream processors; idempotent processors for replayability.
- Metrics & SLOs: time-to-detect (target <2 min for critical), false-positive rate threshold, remediation success rate; dashboards and alerting.
Security & compliance:
- mTLS, agent attestation (TPM/SSH key), signed events, RBAC for remediation runs, audit logs preserved.
Trade-offs:
- Agent vs agentless: agent provides near‑real‑time and local verification at cost of maintenance; agentless (SSH polling) is simpler but higher latency/scale cost.
- Strict auto-remediation reduces manual toil but risks incorrect changes—mitigated by canaries, approvals for high-risk fixes.
Example flow:
Agent detects sshd_config change -> sends diff -> collector buffers -> stream processor verifies baseline & scores as high-risk -> triggers canary remediation on 10 hosts -> successful -> orchestrator scales remediation -> alert created summarizing outcome.
You run an autoscaling group where instances fetch configuration at boot from a central service. Describe how to design bootstrapping so it is idempotent and race-free, how to prevent the config service from being overwhelmed during scale-outs, and how to ensure consistent configuration across short-lived instances.
Sample Answer
Approach/framework:
- Treat bootstrapping as pulling a versioned, immutable artifact (declarative config) and applying it idempotently; protect the config service from load with caching, throttling and staged rollouts; ensure consistency by versioning, verification and readiness gating.
Design details:
- Idempotent, race-free bootstrap
- Use declarative config (e.g., config version v123). Boot script checks current applied version; if matches, exit. Apply changes with idempotent operations (replace files atomically, use systemd units with ExecStartPre checks, database migrations guarded by advisory locks or leader election).
- Use atomic file writes (write to temp + rename), check hashes before applying, and make mutating operations safe for concurrent runs (compare-and-swap or transactions).
- Example: cloud-init fetches /configs/v123.tar.gz, verifies SHA256, extracts to /opt/app/config.new, then move into place and notify systemd.
- Prevent overwhelming config service during scale-outs
- Serve configs via highly scalable read store (S3/Cloud Storage + CDN) rather than a single API. For dynamic secrets/configs: use a small API backed by a cache (Redis) with rate limits and circuit breakers.
- Stagger boots: use instance startup jitter and exponential backoff with randomized delays so thousands don’t hit at once.
- Pre-bake common config into AMIs or container images to reduce runtime fetches.
- Use a fanout queue: instances read a minimal pointer from service, then pull artifact from S3/CDN; for very large scale, use pre-signed URLs to offload auth.
- Ensure consistent config across short-lived instances
- Version every config and artifact; require instances to fetch exact version referenced by ASG launch template or a parameter store value.
- Verify integrity (SHA256/signature) and fail-fast if mismatch; block service readiness until config applied and health checks pass.
- Use health-check gating in the autoscaler: only mark instance healthy/accept traffic after bootstrap success. Monitor drift and run periodic config reconciliation agents to re-apply/correct drift.
Operational considerations & metrics:
- Monitor config service QPS, 5xxs, and S3 4xx/5xx; alert on elevated boot failures, high retry rates, and prolonged bootstrap times.
- Test with chaos: simulate mass scale-out and config-store outage; validate backoff, circuit-breaker, and health-gating behavior.
This design minimizes race conditions, protects the config service at scale, and ensures every instance converges to the same verified configuration.
Design an automated validation and rollback strategy for configuration changes that touch multiple microservices. Define canary rollout criteria, health indicators to watch, automatic rollback triggers, and mechanisms to coordinate rollbacks across dependent services to prevent cascading failures.
Sample Answer
Requirements & constraints:
- Safe automated validation + rollback for config changes touching N microservices.
- Minimize blast radius, prevent cascading failures, ensure fast recovery, maintain SLOs.
High-level approach:
- Use progressive canary rollouts driven by orchestration (Argo Rollouts/Flagd + Kubernetes + CI/CD).
- Protect with feature-flags, traffic-splits, circuit-breakers, and a centralized validation engine that consumes metrics/alerts and executes rollbacks.
Canary rollout criteria:
- Phased percentages: 0% → 1% → 5% → 20% → 50% → 100%. Move to next step only if all validation checks pass for a configurable window (e.g., 5–15 mins).
- Minimum baseline: success threshold (e.g., error rate ≤ baseline + 0.5%), latency p95 ≤ baseline × 1.2, CPU/memory within limits, and SLO burn rate under threshold.
Health indicators to watch:
- Service-level: error rates (4xx/5xx), latency percentiles (p50/p95/p99), throughput, abnormal increases in retries/timeouts.
- Platform-level: pod restarts, OOMs, node pressure, queue/backlog growth, downstream latency.
- Business signals: conversion rate, critical feature success, user-facing errors (if available).
- Observability: logs for new exceptions, tracing spike count.
Automatic rollback triggers:
- Absolute thresholds: error rate > X% or p95 latency increase > Y% for T consecutive windows.
- Relative deviation: sustained degradation vs. baseline (e.g., >2σ or >20% relative).
- SLO burn-rate crossing error-budget window.
- Anomaly detection alert (e.g., AI/ML-based) or health-check failures.
- Any pod crashloop/OOM spike immediately halts rollout and triggers rollback.
Coordinating rollbacks across dependent services:
- Dependency graph: maintain service dependency metadata in the orchestrator.
- Group canaries by dependency tier. If config touches multiple services in a dependency chain, roll them out in order from downstream to upstream, validating each tier.
- On trigger in one service, orchestrator:
- Immediately stop further rollout steps for all related services.
- If degradation impacts downstream consumers, automatically rollback the offending service first.
- If ambiguity, enter semi-automated pause and notify on-call with suggested action and one-click rollback.
- Implement global circuit-breaker: when a critical service hits trigger, traffic to dependent services switches back to previous config via automated traffic-split rollback.
Mechanisms & tooling:
- Argo Rollouts or Spinnaker for progressive delivery + automated analysis hooks.
- Feature flags (LaunchDarkly/Flagd) for instant toggle and safer coordination.
- Prometheus + Alertmanager + Thanos for metrics; Grafana for dashboards and SLOs; Jaeger for traces; ELK for logs.
- A Validation Engine (Lambda/K8s job) that queries metrics/traces/logs, runs checks, and calls CD API to promote/rollback.
- Webhooks & chatops (Slack) with approval/rollback buttons for human-in-the-loop.
Safeguards & tests:
- Run chaos tests and staged rehearsals in staging using real traffic replays.
- Canaries must have automatic rollback cooldowns to avoid flip-flopping.
- Audit logs for all rollouts/rollbacks; require postmortem for rollbacks that cross thresholds.
This design minimizes blast radius, uses objective metrics for promotion/rollback, and coordinates dependent services to prevent cascading failures while keeping human operators in the loop for ambiguous situations.
Compare configuration management tools Puppet, Chef, and Ansible from an SRE perspective. Discuss their architectures (agent vs agentless), push vs pull models, templating capabilities, idempotency guarantees, scaling characteristics, and typical operational trade-offs for large fleets.
Sample Answer
High-level summary: Puppet, Chef, and Ansible are mature configuration management tools but differ in architecture, workflow model, templating, and operational trade-offs. From an SRE viewpoint choose by environment constraints (security, scale, network), team skills, and desired operational model.
Architecture / agent vs agentless
- Puppet: agent-based (puppet agent) with a central Puppet Server (master). Agents pull catalogs from server.
- Chef: agent-based (chef-client) with Chef Server (or hosted). Also supports chef-solo/chef-zero for local runs.
- Ansible: agentless — uses SSH (or WinRM) from a control node to push tasks.
Push vs pull models
- Puppet/Chef: pull model by default (agents periodically request desired state). Good for intermittent connectivity and convergence guarantees.
- Ansible: push model (ad-hoc runs or scheduled via AWX/Tower). Easier for immediate ad-hoc changes, less suitable for disconnected nodes unless pull runners (Ansible Pull) used.
Templating capabilities
- Puppet: Embedded DSL with ERB and EPP templates; strong resource abstraction and modules.
- Chef: Ruby DSL + ERB; highly programmable because recipes are Ruby code.
- Ansible: YAML + Jinja2 templates; simple and readable, good for config files and orchestration.
Idempotency guarantees
- Puppet/Chef: declarative resource models emphasize idempotency — converges toward desired state; idempotency enforced at resource provider level.
- Ansible: many modules are idempotent; playbook authors must be careful with shell/command tasks. Declarative role-based patterns mitigate risk.
Scaling characteristics
- Puppet/Chef: scale well for very large fleets using multiple masters, load balancers, sharding, and database-backed servers; agents reduce concurrency pressure on control plane since nodes pull on staggered schedules.
- Ansible: controller must open concurrent SSH connections; scale via control node tuning, connection pooling, or delegating to AWX/Ansible Tower and execution nodes; less suited to extremely large fleets without additional infrastructure (bastion/proxy or callback runners).
Operational trade-offs for large fleets
- Security & compliance: Agents provide authenticated, scheduled convergence; easier to ensure every node checks in. Agentless reduces footprint but requires SSH keys and network access from control plane.
- Real-time changes: Ansible excels for one-off fixes and orchestration; Puppet/Chef better for continuous convergence.
- Complexity & flexibility: Chef (Ruby) offers most programmability; Puppet has mature module ecosystem and strong reporting; Ansible is simplest to learn and operate.
- Reliability: Pull model handles flaky networks better. Push model requires coordination to avoid thundering-herd; use orchestration tools to batch runs.
- Maintenance: Agents require lifecycle management (upgrades); server components need HA planning. Ansible has fewer moving parts but expect heavier load on control nodes.
- Observability: Puppet/Chef provide built-in reporting and node facts; use this for compliance auditing. Ansible needs orchestration layer for centralized state history.
Recommendation (SRE practical): For strict convergence and huge, partially-connected fleets prefer Puppet/Chef. For fast orchestration, simpler playbooks, or environments with strong SSH/Ansible expertise, choose Ansible but plan for scaling (AWX/Tower, connection brokers) and enforce idempotent module use.
Unlock Full Question Bank
Get access to all 38 Configuration Management and Desired State interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.