DEV Community

Cover image for Setting up sovereign cloud reference architectures: three patterns that work
binadit
binadit

Posted on • Originally published at binadit.com

Setting up sovereign cloud reference architectures: three patterns that work

Three sovereign cloud patterns that actually hold up under audit

Here's the problem: picking "EU" from a cloud console dropdown is not sovereignty. It's a checkbox. Real data residency means your compute, your backups, your third-party services, and your failover path all stay inside a legal boundary, and you can prove it with a packet capture, not just a policy doc.

We've deployed three reference patterns repeatedly for SaaS platforms, agencies, and e-commerce clients dealing with GDPR, DORA, or public-sector requirements. Here's how each one works, with the configs we actually run.

Before you start

You'll need:

  • A stateless app tier (no session affinity dependencies)
  • Terraform/OpenTofu plus Ansible or equivalent
  • A clear map of which data is regulated and which isn't
  • Access to at least one EU-based provider, plus a public cloud account if you're going hybrid
  • Comfort with HAProxy/Nginx and PostgreSQL/MySQL replication

If you haven't decided between open-source infra and managed public cloud yet, sort that out first. Everything below assumes it's settled.

Pattern 1: Single-region EU

The baseline. Good for most SaaS teams that need provable EU residency without multi-region complexity.

Everything (compute, storage, backups, logging, error tracking) stays in one EU region. Spread compute across at least two AZs, never a single rack.

Load balancer tier:

frontend web_front
    bind *:443 ssl crt /etc/haproxy/certs/app.pem
    mode http
    default_backend app_servers

backend app_servers
    balance roundrobin
    option httpchk GET /health
    server app1 10.0.1.11:8080 check
    server app2 10.0.1.12:8080 check
    server app3 10.0.1.13:8080 check
Enter fullscreen mode Exit fullscreen mode

PostgreSQL synchronous replication for zero data loss on failover:

# postgresql.conf on primary
synchronous_standby_names = 'standby1'
wal_level = replica
max_wal_senders = 5

# recovery config on standby
primary_conninfo = 'host=10.0.1.21 port=5432 user=replicator'
Enter fullscreen mode Exit fullscreen mode

The part everyone forgets: route email, error tracking, and analytics through EU-based providers too. A perfectly compliant database means nothing if your error tracker ships stack traces to a US endpoint.

Back up daily, encrypt everything, keep it in-region. 30 days daily / 12 months monthly is a reasonable default for compliance workloads.

Pattern 2: Active-passive multi-region

For disaster recovery across two EU regions (say, Amsterdam and Frankfurt) without paying for a full duplicate stack.

Size the passive region at 30-50% capacity, enough to run degraded, not enough to bleed money idling.

Async replication to the secondary:

# On secondary region standby
primary_conninfo = 'host= port=5432 user=replicator sslmode=require'
restore_command = 'cp /archive/%f %p'
Enter fullscreen mode Exit fullscreen mode

DNS-based failover:

failover_policy:
  primary: eu-west (Amsterdam)
  secondary: eu-central (Frankfurt)
  health_check_interval: 10s
  failover_threshold: 3 consecutive failures
Enter fullscreen mode Exit fullscreen mode

Automate the promotion, don't SSH in under pressure:

pg_ctl promote -D /var/lib/postgresql/data
# update connection strings via config management, never manual edits
Enter fullscreen mode Exit fullscreen mode

And actually test failover quarterly. This is the step everyone skips, and it's the only one that proves the whole thing works.

Pattern 3: Hybrid private-public

Best for regulated workloads (payments, health data, government contracts) that also need elastic burst capacity for traffic spikes.

Keep the system of record (database, PII, payment processing) on private EU infrastructure. Push only stateless, non-sensitive work (static assets, image processing, caching) to public cloud.

Enforce the boundary at the network layer, not just in app code:

# Restrict outbound DB traffic to private subnet only
iptables -A OUTPUT -p tcp --dport 5432 -d 10.0.0.0/8 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 5432 -j DROP
Enter fullscreen mode Exit fullscreen mode

Route burst traffic through a CDN that actually keeps EU traffic on EU edge nodes (not all of them do, check before you commit). Keep a data flow diagram ready; procurement and auditors will ask for it before signing anything.

Verify it, don't just deploy it

  • Residency: traceroute and packet capture a real transaction, confirm no hop leaves the EU
  • Failover time: target under 60 seconds with a 10-second health check interval; over 5 minutes means your thresholds are too conservative
  • Replication lag: SELECT now() - pg_last_xact_replay_timestamp(); on the standby, sustained lag over 5s means trouble on unplanned failover
  • Boundary enforcement: try connecting from the public segment to the private DB port, confirm the firewall (not app logic) rejects it
  • Real downtime: run a drill and measure customer-facing downtime, not just the infra switch; DNS caching often adds 2-5 minutes you didn't account for

Pitfalls worth repeating

  • Selecting "EU" in a console is not proof of anything, verify subprocessors and backup locations contractually and technically
  • Third-party services are the most commonly skipped audit item
  • Under-sizing the passive region means it won't actually hold real traffic when it matters

Full details and more context in the original article.

Originally published on binadit.com

Top comments (0)