DEV Community

Cover image for DIY Consul/etcd Config — When to Stop Building (Architecture)
Moshe Avdiel
Moshe Avdiel

Posted on • Originally published at github.com

DIY Consul/etcd Config — When to Stop Building (Architecture)

Black Friday retrospective. Platform built live config on Consul KV three years ago. Every service watches config/payments/prod/fraud/block_score. It worked — until catalog pods hammered Consul with long-poll watchers, JVM services added 30s local cache TTL "to be safe," and fraud raised block_score in KV while authorization still read stale 88 for half a minute.

The infra lead:

"We are not bad engineers. We are product engineers for a config product we never meant to build. When do we stop DIY and adopt a hub whose only job is live config?"

This buyer guide is the stop line companion to Kiponos vs Consul/etcd. Consul and etcd remain excellent for service discovery and coordination. Kiponos.io replaces the DIY config layer teams bolt on top — dashboards, schemas, watch loops, cache coherency debates. Evaluate during Black Friday stale KV — evaluate hub vs DIY.

The problem — KV watch is not a product

@Scheduled(fixedDelay = 5000)
public void refreshConfig() {
    String raw = consulClient.getKVValue("config/payments/fraud/block_score").getValue();
    this.blockScore = Integer.parseInt(base64Decode(raw));
}

public Decision authorize(int riskScore) {
    if (riskScore >= blockScore) {  // may be 30s stale with cache layered on
        return Decision.block();
    }
    return Decision.approve();
}
Enter fullscreen mode Exit fullscreen mode

Teams building on KV inherit:

  • Key naming conventions that diverge per service
  • Custom dashboards or raw consulctl
  • Schema validation gaps — strings in KV
  • Cache coherency wars — TTL vs watch vs dual-write
  • Watch storms under churn — Black Friday catalog nightmare

The cost is not Consul licensing. It is headcount operating a config product on coordination primitives.

What teams believe vs production reality

Belief Production reality
"Consul watch is real-time" Watch storms → teams add TTL cache → staleness
"We already run Consul for mesh" Repurposing SD couples discovery outages to config
"KV is free self-hosted" Engineering tax for dashboard, ACL, schema
"30s cache is safe" Fraud raised score; auth read stale 88 — revenue loss
"We are almost done with the platform" Config platforms are never finished

The Aha

Consul coordinates infrastructure. A live config hub operates application behavior. Keep Consul for service catalog. Move operational thresholds to a purpose-built hub with WebSocket deltas and local SDK reads — stop paying the DIY tax when the stop-line signals fire.

Stop-line checklist — when to stop building DIY

Stop extending DIY Consul/etcd config when three or more apply:

# Stop signal What it costs you
1 You added local TTL cache because watches hammered KV Staleness bugs on hot path
2 Two teams invented incompatible key hierarchies Onboarding tax
3 On-call runbook includes "restart watchers" Reliability debt
4 Fraud/SRE needs sub-minute edits during incidents KV + cache too slow
5 You built a custom dashboard for non-engineers Permanent maintenance
6 Java and Python duplicate watch boilerplate Polyglot tax
7 Postmortem cites stale config as contributing factor Business risk
8 Platform roadmap has "config v2" for third year Opportunity cost

Three or more → evaluate Kiponos.io for ops keys; keep Consul for SD.

Architecture — DIY watch vs hub delta

Architecture diagram

Decision table — keep Consul vs migrate ops keys

Capability Keep on Consul/etcd Migrate to Kiponos
Service registration Yes No
Health checks Yes No
Leader election / locks Yes No
fraud.block_score No Yes
Saga timeout trees No Yes
Per-tenant RPM limits No Yes
Bootstrap service name Yes No
ML batch_size on worker No Yes

Config tree — replace slash-separated KV

Consul today:

config/payments/prod/fraud/thresholds/block_score = 82
config/payments/prod/saga/inventory/timeout_ms = 45000
Enter fullscreen mode Exit fullscreen mode

Kiponos tree:

payments_ops/
  fraud/
    thresholds/
      block_score: 82
      review_score: 67
  resilience/
    partner/
      failure_rate_threshold: 38
  saga/
    inventory_compensation/
      timeout_ms: 45000
      max_retries: 3
  limits/
    tenant_acme/
      rpm: 6000
Enter fullscreen mode Exit fullscreen mode

Profile path: ['payments']['prod']['live'].

Integration — delete the scheduled Consul poll

@Configuration
public class KiponosConfig {
    @Bean
    public Kiponos kiponos(
            @Value("${kiponos.team-id}") String teamId,
            @Value("${kiponos.access-key}") String accessKey) {
        return Kiponos.builder()
            .teamId(teamId)
            .accessKey(accessKey)
            .profilePath("['payments']['prod']['live']")
            .build();
    }
}

@Service
public class FraudGate {
    private final Kiponos kiponos;

    public Decision evaluate(int riskScore) {
        int block = kiponos.path("payments_ops", "fraud", "thresholds")
            .getInt("block_score");
        if (riskScore >= block) return Decision.block();
        return Decision.approve();
    }
}
Enter fullscreen mode Exit fullscreen mode
import os
from kiponos import Kiponos

os.environ["KIPONOS_PROFILE"] = "['payments']['prod']['live']"
kiponos = Kiponos.create_for_current_team()

def inventory_compensation_timeout_ms() -> int:
    return kiponos.path(
        "payments_ops", "saga", "inventory_compensation"
    ).get_int("timeout_ms", 45000)
Enter fullscreen mode Exit fullscreen mode

No consul watch sidecar beside Celery workers.

Real scenarios — DIY pain vs hub

Event DIY Consul KV Kiponos hub
Black Friday stale KV 30s cache lie on block_score Delta; next getInt() immediate
Raise fraud score mid-incident KV put; caches stale Dashboard seconds
Consul outage during peak Reads fail or stale SDK serves last merged tree
New engineer onboards Key conventions + watchers Dashboard + path() API
Cross-service saga timeout Six flat keys to grep payments_ops/saga/* tree
Service discovery refresh Consul native Not Kiponos job

Performance — watch storms vs local read

  • Consul long-poll — server load scales with watcher count
  • DIY TTL cache — trades staleness for load — the Black Friday bug
  • etcd gRPC watch — better streams; still not in-process getInt() without cache
  • Kiponos delta — one key patch; no per-service watcher fleet
  • Read path — O(1) SDK memory beside authorization logic

Compare to alternatives

Criterion DIY Consul/etcd KV Kiponos Verdict
Service discovery Excellent Not replacement Keep Consul
DIY config on KV Flexible, you operate Managed hub Stop DIY when checklist hits
Hot-path local read RTT unless cached (stale) SDK Kiponos
Typed tree + dashboard Build yourself Built-in Kiponos
Operational headcount High Lower TCO at stop line

When not to use Kiponos

Use case Better tool
Service registration and health Consul
Distributed locks etcd
Mesh certificate rotation Consul / Istio
Infrastructure desired state GitOps
Air-gap mandate to self-host KV only Evaluate private hub deploy vs DIY

Getting started (15 minutes) — evaluate at stop line

  1. Run stop-line checklist — count signals ≥ 3.
  2. Inventory Consul keys under config/* — mark ops vs bootstrap.
  3. TeamPro at kiponos.io — profile ['payments']['prod']['live'].
  4. Migrate three keys: block_score, one saga timeout, one tenant RPM.
  5. Delete @Scheduled Consul poll in one service; measure hot-path latency.
  6. Leave Consul service catalog untouched — config migration only.
  7. Present TCO slide: DIY dashboard + on-call hours vs hub subscription.

Further reading


Kiponos.io — Consul for where services live. Stop building the config product on top when stale cache writes your postmortem.

Top comments (0)