๐ Introduction
When I started my journey into Site Reliability Engineering (SRE), I quickly realized that understanding how systems behave in distributed environments is one of the most valuable skills you can have. But there's a catch: setting up a realistic multi-server environment to practice high availability (HA) and disaster recovery (DR) is expensive, complex, and time-consuming.
So I decided to build my own.
This article is about how I created a fully automated 2-data center lab that runs entirely on my laptop using Vagrant and VirtualBox. It simulates a production-like architecture with PostgreSQL replication, a load balancer, and dynamic DNSโall with a single command: vagrant up.
๐ฏ What Problem Does This Solve?
In the real world, infrastructure engineers, SREs, and DevOps practitioners need to understand how systems behave when things go wrong. But:
- Cloud environments cost money when you leave them running.
- Physical hardware is expensive and not portable.
- Manual setups are error-prone and hard to reproduce.
This lab solves all of those problems. It's:
- Free (open source).
- Portable (runs on your laptop).
- Reproducible (everything is code).
- Safe (you can break it and rebuild it in seconds).
๐๏ธ The Architecture
The lab consists of 6 virtual machines distributed across two simulated data centers:
-
DC1:
db1(PostgreSQL Primary) +app1(Flask App) -
DC2:
db2(PostgreSQL Replica) +app2(Flask App) -
Common Infrastructure:
lb(HAProxy Load Balancer) +dns(Bind9 DNS Server)
Network:
- DC1:
10.10.1.0/24 - DC2:
10.10.2.0/24
This separation simulates two physically distinct locations, allowing me to test real-world scenarios like network partitions and data center failures.
๐ ๏ธ Technologies Used
- Vagrant โ Infrastructure orchestration.
- VirtualBox โ Hypervisor.
- PostgreSQL 16 โ Streaming replication (Active/Passive).
- Flask โ Python API application.
- HAProxy โ Load balancing (round-robin).
- Bind9 โ Dynamic DNS resolution.
๐งช What Can You Test?
This lab is designed to be a safe playground for real-world scenarios:
1. Controlled Switchover
Promote the replica to primary with zero downtime.
vagrant ssh db1
sudo -u postgres psql -c "SELECT pg_switch_wal();"
sudo systemctl stop postgresql
exit
vagrant ssh db2
sudo pg_ctl promote -D /var/lib/postgresql/16/main
2. Disaster Recovery (DR)
Simulate the complete loss of DC1 and verify that the system continues to work using DC2 alone.
vagrant halt db1 app1
vagrant ssh db2
sudo pg_ctl promote -D /var/lib/postgresql/16/main
curl http://10.10.2.10:5000/items
3. Dynamic DNS Failover
Update Bind9 records to point to the new primary database:
nsupdate -k /etc/bind/keys.conf << EOF
update delete db.lab.local A
update add db.lab.local 5 A 10.10.2.20
send
EOF
4. Node Recovery
Rejoin a failed primary node back into the cluster as a replica after recovery.
๐ The Code
Everything is open source and available on GitHub:
๐ https://github.com/rafajimenezdev/2dc-ha-lab
Repository structure:
2dc-ha-lab/
โโโ README.md # Full documentation
โโโ LICENSE # MIT License
โโโ Vagrantfile # VM definitions
โโโ images/
โ โโโ arch_whitebg.png # Architecture diagram
โโโ provision/
โ โโโ db1.sh # Primary DB setup
โ โโโ db2.sh # Replica DB setup
โ โโโ app.sh # Flask app setup
โ โโโ lb.sh # HAProxy setup
โ โโโ dns.sh # Bind9 DNS setup
โโโ app/
โโโ app.py # Flask application
โโโ requirements.txt # Python dependencies
๐ How to Get Started
1. Clone the repository
git clone https://github.com/rafajimenezdev/2dc-ha-lab.git
cd 2dc-ha-lab
2. Start the environment
vagrant up
3. Test the application
curl http://10.10.1.100/items
4. Insert data
curl -X POST http://10.10.1.100/items -H "Content-Type: application/json" -d '{"id": 42}'
๐ Validation Tests
After vagrant up, run these tests to ensure everything is working:
# Check VM status
vagrant status
# Test connectivity
ping 10.10.1.10
ping 10.10.2.10
# Test load balancer
for i in {1..5}; do curl http://10.10.1.100/; done
# Test API
curl http://10.10.1.100/items
curl -X POST http://10.10.1.100/items -H "Content-Type: application/json" -d '{"id": 42}'
curl http://10.10.1.100/items
# Verify replication
curl http://10.10.2.10:5000/items
๐ Common Issues & Fixes
SSH Timeouts During Boot
Problem: Vagrant gets stuck at SSH auth method: private key.
Solution: Add this to your Vagrantfile:
vb.customize ["modifyvm", :id, "--cableconnected1", "on"]
Workaround: If the VM gets stuck, open the VirtualBox GUI and bring the VM window to the foreground.
VirtualBox Host-Only Adapter Conflicts
Problem: Error VERR_INTNET_FLT_IF_NOT_FOUND.
Solution:
- Open VirtualBox โ File โ Tools โ Network Manager.
- Go to Host-only Networks.
- Ensure you have two adapters:
-
10.10.1.1/24(DC1) -
10.10.2.1/24(DC2)
-
- Disable DHCP on both.
Hyper-V Conflict
Problem: VirtualBox fails to start VMs.
Solution: Disable Hyper-V:
bcdedit /set hypervisorlaunchtype off
๐ก Lessons Learned
Infrastructure as Code is powerful. Being able to destroy and recreate the entire environment with a single command (
vagrant destroy -f && vagrant up) changes the way you think about testing and experimentation.DNS is critical in failover scenarios. Hardcoding IPs is a trap. Using dynamic DNS (
nsupdate) makes the system much more resilient and realistic.Simplicity wins. While it would be easy to add more complexity (e.g., Kubernetes, service meshes), starting with a simple, solid foundation helps you understand the fundamentals.
The community is the best resource. Sharing this project on Reddit and other platforms has given me invaluable feedback and new ideas.
๐ค What's Next?
I'm planning to:
- Add
tc(traffic control) to simulate network latency and packet loss between DCs. - Write a follow-up article on "Chaos Engineering in a Local Lab."
- Create a video demo showing the failover and DR scenarios in action.
๐ข Let's Connect!
If you enjoyed this article or have questions about the lab, feel free to reach out:
- GitHub: rafajimenezdev
- LinkedIn: Rafael Jimรฉnez Pรฉrez
- Reddit: Discussion on Reddit about the project
Built with โค๏ธ for the infrastructure community.
Top comments (0)