NEWS Earn Money with Onidel Cloud! Affiliate Program Details - Check it out

How to Build an Active‑Active Amsterdam VPS ↔ New York VPS Architecture: GeoDNS/Anycast, HAProxy, PostgreSQL 17 Logical Replication, and S3 Object Replication on Ubuntu 24.04 (2025 Tutorial)

Building a truly active-active architecture across continents requires careful orchestration of DNS routing, load balancing, database replication, and object storage synchronization. In this comprehensive tutorial, we’ll deploy a production-ready active-active setup between Onidel Amsterdam VPS and Onidel New York VPS instances, leveraging GeoDNS for intelligent traffic routing, HAProxy for load balancing, PostgreSQL 17’s enhanced logical replication for bidirectional database synchronization, and MinIO for S3-compatible object storage replication.

Why Active-Active Architecture Matters

Traditional active-passive setups leave resources idle and introduce failover delays. Active-active architectures maximize resource utilization while providing zero-downtime resilience and improved user experience through geographic proximity. This setup is particularly valuable for applications serving both European and North American markets, where Amsterdam VPS vs New York VPS latency differences can significantly impact performance.

Prerequisites

Before beginning this tutorial, ensure you have:

  • Two Ubuntu 24.04 LTS VPS instances: One in Amsterdam and one in New York (minimum 4GB RAM, 2 vCPUs each)
  • Domain name with DNS provider supporting GeoDNS or Anycast (Cloudflare, Route 53, NS1)
  • Root access on both servers
  • SSL certificates for your domain
  • S3-compatible storage buckets in both regions

Architecture Overview

Our active-active setup consists of:

  • GeoDNS/Anycast: Routes users to nearest region
  • HAProxy: Load balancing and health checks
  • PostgreSQL 17: Bidirectional logical replication
  • MinIO: Cross-region object storage replication
  • Application servers: Containerized web services

Step 1: System Preparation

First, update both servers and install required packages:

# On both Amsterdam and New York VPS
sudo apt update && sudo apt upgrade -y
sudo apt install -y haproxy postgresql-17 postgresql-client-17 docker.io docker-compose-v2

# Configure system limits
echo "* soft nofile 65536" | sudo tee -a /etc/security/limits.conf
echo "* hard nofile 65536" | sudo tee -a /etc/security/limits.conf

# Enable IP forwarding for HAProxy
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Step 2: GeoDNS/Anycast Setup

Configure DNS routing using Cloudflare’s GeoDNS. Create A records for your domain:

# Amsterdam VPS record (Europe traffic)
Type: A
Name: @
IPv4: YOUR_AMSTERDAM_IP
TTL: 60 seconds
Region: Europe

# New York VPS record (Americas traffic)
Type: A
Name: @
IPv4: YOUR_NEWYORK_IP
TTL: 60 seconds
Region: Americas

For health-based failover, enable Cloudflare Health Checks. This ensures traffic automatically routes to the healthy region if one becomes unavailable.

Step 3: HAProxy Load Balancer Configuration

Configure HAProxy on both servers. Create /etc/haproxy/haproxy.cfg:

global
    daemon
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660
    stats timeout 30s
    user haproxy
    group haproxy
    
    # TLS configuration
    ssl-default-bind-ciphers ECDHE+AESGCM:ECDHE+CHACHA20:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets

defaults
    mode http
    timeout connect 5000
    timeout client 50000
    timeout server 50000
    compression algo gzip
    compression type text/html text/plain text/css text/javascript application/javascript

frontend web_frontend
    bind *:80
    bind *:443 ssl crt /etc/ssl/private/yourdomain.pem
    redirect scheme https if !{ ssl_fc }
    
    # Health check endpoint
    acl is_health_check path /health
    http-request return status 200 content-type "text/plain" string "OK" if is_health_check
    
    default_backend web_servers

backend web_servers
    balance roundrobin
    option httpchk GET /health
    
    # Local application servers
    server app1 127.0.0.1:8080 check
    server app2 127.0.0.1:8081 check
    
    # Cross-region failover (adjust IPs)
    server remote_app1 REMOTE_VPS_IP:8080 check backup
    server remote_app2 REMOTE_VPS_IP:8081 check backup

Start and enable HAProxy:

sudo systemctl enable haproxy
sudo systemctl start haproxy

Step 4: PostgreSQL 17 Logical Replication

PostgreSQL 17 introduces enhanced logical replication capabilities. Configure bidirectional replication between both regions.

Database Initialization

On both servers, initialize PostgreSQL:

# Initialize cluster
sudo pg_createcluster 17 main
sudo systemctl start postgresql@17-main
sudo systemctl enable postgresql@17-main

# Configure PostgreSQL
sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'your_secure_password';"
sudo -u postgres createdb appdb

Replication Configuration

Edit /etc/postgresql/17/main/postgresql.conf on both servers:

# Replication settings
wal_level = logical
max_wal_senders = 4
max_replication_slots = 4
max_logical_replication_workers = 4
max_worker_processes = 8

# Performance tuning
shared_preload_libraries = 'pg_logical'
log_logical_decoding_work_mem = 64MB

Configure /etc/postgresql/17/main/pg_hba.conf:

# Replication connections
host    replication     postgres        AMSTERDAM_IP/32         md5
host    replication     postgres        NEWYORK_IP/32           md5
host    appdb           postgres        AMSTERDAM_IP/32         md5
host    appdb           postgres        NEWYORK_IP/32           md5

Logical Replication Setup

# Amsterdam VPS: Create publication
sudo -u postgres psql -d appdb -c "CREATE PUBLICATION amsterdam_pub FOR ALL TABLES;"

# New York VPS: Create publication  
sudo -u postgres psql -d appdb -c "CREATE PUBLICATION newyork_pub FOR ALL TABLES;"

# Amsterdam VPS: Subscribe to New York
sudo -u postgres psql -d appdb -c "
CREATE SUBSCRIPTION newyork_sub 
CONNECTION 'host=NEWYORK_IP dbname=appdb user=postgres password=your_secure_password' 
PUBLICATION newyork_pub;"

# New York VPS: Subscribe to Amsterdam
sudo -u postgres psql -d appdb -c "
CREATE SUBSCRIPTION amsterdam_sub 
CONNECTION 'host=AMSTERDAM_IP dbname=appdb user=postgres password=your_secure_password' 
PUBLICATION amsterdam_pub;"

Step 5: S3 Object Storage Replication

Deploy MinIO for S3-compatible object storage with cross-region replication:

# docker-compose.yml for MinIO
version: '3.8'
services:
  minio:
    image: minio/minio:RELEASE.2024-01-16T16-07-38Z
    ports:
      - "9000:9000"
      - "9001:9001"
    volumes:
      - minio_data:/data
    environment:
      MINIO_ROOT_USER: admin
      MINIO_ROOT_PASSWORD: your_secure_password
    command: server /data --console-address ":9001"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

volumes:
  minio_data:

Configure bucket replication using MinIO client:

# Install MinIO client
wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin/

# Configure aliases
mc alias set amsterdam http://AMSTERDAM_IP:9000 admin your_secure_password
mc alias set newyork http://NEWYORK_IP:9000 admin your_secure_password

# Create buckets
mc mb amsterdam/app-assets
mc mb newyork/app-assets

# Enable versioning
mc version enable amsterdam/app-assets
mc version enable newyork/app-assets

# Configure replication
mc replicate add amsterdam/app-assets --remote-bucket newyork/app-assets
mc replicate add newyork/app-assets --remote-bucket amsterdam/app-assets

Step 6: Application Deployment

Deploy your application stack with Docker Compose. Ensure applications are database-region-aware to avoid write conflicts:

# Application docker-compose.yml
version: '3.8'
services:
  app1:
    image: your-app:latest
    ports:
      - "8080:8080"
    environment:
      - DB_HOST=localhost
      - DB_NAME=appdb
      - REGION=amsterdam # or newyork
      - S3_ENDPOINT=http://localhost:9000
    depends_on:
      - postgres
      
  app2:
    image: your-app:latest  
    ports:
      - "8081:8081"
    environment:
      - DB_HOST=localhost
      - DB_NAME=appdb
      - REGION=amsterdam # or newyork
      - S3_ENDPOINT=http://localhost:9000

Best Practices

  • Conflict Resolution: Implement application-level conflict resolution for write operations
  • Regional Data Affinity: Use region-specific primary keys (e.g., AMS_001, NYC_001 prefixes)
  • Monitoring: Deploy comprehensive monitoring with observability stack
  • Backup Strategy: Implement point-in-time recovery backups for both regions
  • Security: Enable TLS for all inter-region communications
  • Health Checks: Configure comprehensive health monitoring for automatic failover

Warning: Never perform destructive operations without testing in a staging environment first. Bidirectional replication can amplify mistakes across both regions.

Conclusion

You’ve successfully built a production-ready active-active architecture spanning Amsterdam and New York regions. This setup provides zero-downtime resilience, optimal user experience through geographic proximity, and efficient resource utilization. The combination of GeoDNS routing, HAProxy load balancing, PostgreSQL 17 logical replication, and MinIO object storage creates a robust foundation for global applications.

Ready to deploy your own active-active architecture? Explore our high-performance Amsterdam VPS and New York VPS options, featuring EPYC Milan processors, NVMe storage, and advanced networking capabilities perfect for demanding multi-region deployments.

Share your love