<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rafa Jiménez</title>
    <description>The latest articles on DEV Community by Rafa Jiménez (@rafajimenezdev).</description>
    <link>https://dev.to/rafajimenezdev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4026859%2F8a363ed3-ba49-4fdb-97fa-79f3cb06d0fc.jpg</url>
      <title>DEV Community: Rafa Jiménez</title>
      <link>https://dev.to/rafajimenezdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rafajimenezdev"/>
    <language>en</language>
    <item>
      <title>How I built a Fully Automated 2-Data Center HA Lab with PostgreSQL, HAProxy, and Bind9 (using Vagrant)</title>
      <dc:creator>Rafa Jiménez</dc:creator>
      <pubDate>Mon, 13 Jul 2026 07:59:57 +0000</pubDate>
      <link>https://dev.to/rafajimenezdev/how-i-built-a-fully-automated-2-data-center-ha-lab-with-postgresql-haproxy-and-bind9-using-1cle</link>
      <guid>https://dev.to/rafajimenezdev/how-i-built-a-fully-automated-2-data-center-ha-lab-with-postgresql-haproxy-and-bind9-using-1cle</guid>
      <description>&lt;h2&gt;
  
  
  📖 Introduction
&lt;/h2&gt;

&lt;p&gt;When I started my journey into Site Reliability Engineering (SRE), I quickly realized that understanding &lt;strong&gt;how systems behave in distributed environments&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;So I decided to build my own.&lt;/p&gt;

&lt;p&gt;This article is about how I created a &lt;strong&gt;fully automated 2-data center lab&lt;/strong&gt; 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: &lt;code&gt;vagrant up&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 What Problem Does This Solve?
&lt;/h2&gt;

&lt;p&gt;In the real world, infrastructure engineers, SREs, and DevOps practitioners need to understand how systems behave when things go wrong. But:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloud environments cost money&lt;/strong&gt; when you leave them running.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Physical hardware is expensive&lt;/strong&gt; and not portable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual setups are error-prone&lt;/strong&gt; and hard to reproduce.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This lab solves all of those problems. It's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free&lt;/strong&gt; (open source).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Portable&lt;/strong&gt; (runs on your laptop).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproducible&lt;/strong&gt; (everything is code).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safe&lt;/strong&gt; (you can break it and rebuild it in seconds).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🏗️ The Architecture
&lt;/h2&gt;

&lt;p&gt;The lab consists of &lt;strong&gt;6 virtual machines&lt;/strong&gt; distributed across two simulated data centers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DC1:&lt;/strong&gt; &lt;code&gt;db1&lt;/code&gt; (PostgreSQL Primary) + &lt;code&gt;app1&lt;/code&gt; (Flask App)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DC2:&lt;/strong&gt; &lt;code&gt;db2&lt;/code&gt; (PostgreSQL Replica) + &lt;code&gt;app2&lt;/code&gt; (Flask App)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Common Infrastructure:&lt;/strong&gt; &lt;code&gt;lb&lt;/code&gt; (HAProxy Load Balancer) + &lt;code&gt;dns&lt;/code&gt; (Bind9 DNS Server)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Network:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DC1: &lt;code&gt;10.10.1.0/24&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;DC2: &lt;code&gt;10.10.2.0/24&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation simulates two physically distinct locations, allowing me to test real-world scenarios like network partitions and data center failures.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Technologies Used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vagrant&lt;/strong&gt; – Infrastructure orchestration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VirtualBox&lt;/strong&gt; – Hypervisor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL 16&lt;/strong&gt; – Streaming replication (Active/Passive).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flask&lt;/strong&gt; – Python API application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HAProxy&lt;/strong&gt; – Load balancing (round-robin).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bind9&lt;/strong&gt; – Dynamic DNS resolution.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧪 What Can You Test?
&lt;/h2&gt;

&lt;p&gt;This lab is designed to be a &lt;strong&gt;safe playground&lt;/strong&gt; for real-world scenarios:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Controlled Switchover
&lt;/h3&gt;

&lt;p&gt;Promote the replica to primary with zero downtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vagrant ssh db1
&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; postgres psql &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"SELECT pg_switch_wal();"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop postgresql
&lt;span class="nb"&gt;exit

&lt;/span&gt;vagrant ssh db2
&lt;span class="nb"&gt;sudo &lt;/span&gt;pg_ctl promote &lt;span class="nt"&gt;-D&lt;/span&gt; /var/lib/postgresql/16/main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Disaster Recovery (DR)
&lt;/h3&gt;

&lt;p&gt;Simulate the complete loss of DC1 and verify that the system continues to work using DC2 alone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vagrant halt db1 app1
vagrant ssh db2
&lt;span class="nb"&gt;sudo &lt;/span&gt;pg_ctl promote &lt;span class="nt"&gt;-D&lt;/span&gt; /var/lib/postgresql/16/main
curl http://10.10.2.10:5000/items
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Dynamic DNS Failover
&lt;/h3&gt;

&lt;p&gt;Update Bind9 records to point to the new primary database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nsupdate &lt;span class="nt"&gt;-k&lt;/span&gt; /etc/bind/keys.conf &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
update delete db.lab.local A
update add db.lab.local 5 A 10.10.2.20
send
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Node Recovery
&lt;/h3&gt;

&lt;p&gt;Rejoin a failed primary node back into the cluster as a replica after recovery.&lt;/p&gt;




&lt;h2&gt;
  
  
  📂 The Code
&lt;/h2&gt;

&lt;p&gt;Everything is open source and available on GitHub:&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://github.com/rafajimenezdev/2dc-ha-lab" rel="noopener noreferrer"&gt;https://github.com/rafajimenezdev/2dc-ha-lab&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository structure:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 How to Get Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Clone the repository
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/rafajimenezdev/2dc-ha-lab.git
&lt;span class="nb"&gt;cd &lt;/span&gt;2dc-ha-lab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Start the environment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vagrant up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Test the application
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://10.10.1.100/items
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Insert data
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://10.10.1.100/items &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"id": 42}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📊 Validation Tests
&lt;/h2&gt;

&lt;p&gt;After &lt;code&gt;vagrant up&lt;/code&gt;, run these tests to ensure everything is working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check VM status&lt;/span&gt;
vagrant status

&lt;span class="c"&gt;# Test connectivity&lt;/span&gt;
ping 10.10.1.10
ping 10.10.2.10

&lt;span class="c"&gt;# Test load balancer&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..5&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;curl http://10.10.1.100/&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# Test API&lt;/span&gt;
curl http://10.10.1.100/items
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://10.10.1.100/items &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"id": 42}'&lt;/span&gt;
curl http://10.10.1.100/items

&lt;span class="c"&gt;# Verify replication&lt;/span&gt;
curl http://10.10.2.10:5000/items
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🐛 Common Issues &amp;amp; Fixes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SSH Timeouts During Boot
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Vagrant gets stuck at &lt;code&gt;SSH auth method: private key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Add this to your Vagrantfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;vb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;customize&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"modifyvm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--cableconnected1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"on"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Workaround:&lt;/strong&gt; If the VM gets stuck, open the VirtualBox GUI and bring the VM window to the foreground.&lt;/p&gt;

&lt;h3&gt;
  
  
  VirtualBox Host-Only Adapter Conflicts
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Error &lt;code&gt;VERR_INTNET_FLT_IF_NOT_FOUND&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open VirtualBox → File → Tools → Network Manager.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Host-only Networks&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Ensure you have two adapters:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;10.10.1.1/24&lt;/code&gt; (DC1)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;10.10.2.1/24&lt;/code&gt; (DC2)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Disable DHCP on both.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Hyper-V Conflict
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; VirtualBox fails to start VMs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Disable Hyper-V:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;bcdedit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;hypervisorlaunchtype&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;off&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  💡 Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Infrastructure as Code is powerful.&lt;/strong&gt; Being able to destroy and recreate the entire environment with a single command (&lt;code&gt;vagrant destroy -f &amp;amp;&amp;amp; vagrant up&lt;/code&gt;) changes the way you think about testing and experimentation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DNS is critical in failover scenarios.&lt;/strong&gt; Hardcoding IPs is a trap. Using dynamic DNS (&lt;code&gt;nsupdate&lt;/code&gt;) makes the system much more resilient and realistic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simplicity wins.&lt;/strong&gt; 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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The community is the best resource.&lt;/strong&gt; Sharing this project on Reddit and other platforms has given me invaluable feedback and new ideas.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🤝 What's Next?
&lt;/h2&gt;

&lt;p&gt;I'm planning to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;tc&lt;/code&gt; (traffic control) to simulate network latency and packet loss between DCs.&lt;/li&gt;
&lt;li&gt;Write a follow-up article on "Chaos Engineering in a Local Lab."&lt;/li&gt;
&lt;li&gt;Create a video demo showing the failover and DR scenarios in action.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📢 Let's Connect!
&lt;/h2&gt;

&lt;p&gt;If you enjoyed this article or have questions about the lab, feel free to reach out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/rafajimenezdev" rel="noopener noreferrer"&gt;rafajimenezdev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://linkedin.com/in/rafajimenezpe" rel="noopener noreferrer"&gt;Rafael Jiménez Pérez&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reddit:&lt;/strong&gt; &lt;a href="https://www.reddit.com/r/homelab/comments/1uuq1tg/comment/ox5nxv1/?context=3&amp;amp;utm_source=share&amp;amp;utm_medium=web3x&amp;amp;utm_name=web3xcss&amp;amp;utm_term=1&amp;amp;utm_content=share_button" rel="noopener noreferrer"&gt;Discussion on Reddit about the project&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Built with ❤️ for the infrastructure community.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>vagrant</category>
      <category>postgres</category>
      <category>bind9</category>
      <category>dns</category>
    </item>
  </channel>
</rss>
