Console Login

Zero-Downtime Database Migration: A Survival Guide for Norwegian DevOps

Zero-Downtime Database Migration: A Survival Guide for Norwegian DevOps

I have seen servers melt. I have watched a simple pg_restore turn into a 14-hour nightmare because the destination disk IOPS hit a wall. In the world of systems administration, database migration is the single most anxiety-inducing task you will face. It is open-heart surgery on a patient that is arguably still running a marathon.

If you are moving data within the Nordic region—perhaps migrating a legacy stack from a sluggish provider to a high-performance VPS Norway instance—latency and data sovereignty are not just buzzwords. They are the difference between a successful cutover and a phone call from Datatilsynet.

Most tutorials suggest a "Dump and Restore" strategy. For a 500MB blog? Fine. For a 200GB e-commerce database handling transactions in Oslo? That is professional suicide. We are going to discuss the only way to move a production database in 2024 without losing your sanity: Logical Replication.

The Hardware Bottleneck: Why Your Destination Matters

Before touching a single config file, look at your target infrastructure. Database writes are expensive. During a migration, you are essentially replaying years of history in hours. If your target drive is a spinning rust HDD or a network-throttled shared volume, you will fail.

We use CoolVDS as the reference architecture here because they enforce KVM virtualization with local NVMe storage. When you are syncing Write Ahead Logs (WAL), you need low latency. The round-trip time (RTT) between your old server and your new CoolVDS instance in Oslo should be tested immediately.

ping -c 10 185.x.x.x

If you see jitter above 20ms within Norway, check your routing. Low latency is critical for synchronous replication, though for this guide, we will use asynchronous replication to minimize impact on the master.

Step 1: The Architecture of Logical Replication

We will assume a PostgreSQL 16 environment, which is standard for serious workloads in August 2024. The concept applies equally to MySQL GTID replication, but the implementation details differ.

Instead of locking the database for a dump, we establish a stream. The source server (Publisher) sends changes to the destination server (Subscriber) on CoolVDS. This allows the old server to stay live while the new one catches up.

Pro Tip: Always set the TCP Keepalive settings aggressively in your SSH tunnels or connection strings. Silent connection drops during the initial data sync will force you to restart the entire process.

Step 2: Preparing the Source (The Master)

First, modify your `postgresql.conf` on the source. You need to enable logical replication. This requires a restart, so schedule this one brief window of downtime (or do it during a maintenance window).

# postgresql.conf

# Minimal level for logical replication
wal_level = logical

# Ensure you have enough slots
max_replication_slots = 10

# Enough senders for the connections
max_wal_senders = 10

# Critical for performance
listen_addresses = '*'

Next, configure `pg_hba.conf` to allow the CoolVDS IP address to connect. Do not open this to the world 0.0.0.0/0. Security is paramount.

# pg_hba.conf
host    replication     all             185.x.x.x/32            scram-sha-256

Reload the configuration:

systemctl reload postgresql

Step 3: Snapshot and Schema

Do not move the data yet. Move the structure. The schema needs to exist on the destination before data starts flowing. Use `pg_dump` with the `--schema-only` flag.

pg_dump -h old-db.example.com -U postgres \
    --schema-only \
    --no-owner \
    --no-acl \
    dbname | psql -h new-coolvds-db.no -U postgres dbname

Now, create the publication on the source database:

CREATE PUBLICATION my_migration_pub FOR ALL TABLES;

Step 4: The Sync (The Heavy Lifting)

On your new NVMe storage backed instance, create the subscription. This is where the magic happens. The database will pull existing data and then switch to streaming live updates.

-- Run this on the DESTINATION (CoolVDS)
CREATE SUBSCRIPTION my_migration_sub 
CONNECTION 'host=old-db.example.com port=5432 dbname=prod_db user=replicator password=SecretPassword!' 
PUBLICATION my_migration_pub;

Check the status of the replication immediately:

SELECT * FROM pg_stat_subscription;

You are looking for the state to move from `init` to `streaming`. If it hangs in `init`, check your logs. It is usually a firewall issue or a timeout.

Comparison: Dump vs. Replication

Feature pg_dump / Restore Logical Replication
Downtime Hours (Size dependent) Seconds (Switchover only)
Complexity Low Medium/High
Performance Hit High I/O on read Moderate CPU/Net
Rollback Impossible without data loss Instant (keep old live)

Step 5: The Cutover

Once the replication lag is near zero, you are ready. Monitor the lag with this query:

SELECT pid, application_name, state, 
       (pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) / 1024 / 1024)::float as lag_mb
FROM pg_stat_replication;

When lag_mb is effectively 0, follow this sequence strictly:

  1. Stop the Application: Put your web app in maintenance mode. 503 HTTP status.
  2. Lock the Source: Prevent any stray writes.
  3. Verify Sync: Ensure the LSNs match exactly.
  4. Update DNS/Config: Point your application to the new CoolVDS IP.
  5. Promote Destination: Drop the subscription.

Command to detach the new DB from the old one:

DROP SUBSCRIPTION my_migration_sub;

Reset your sequences (this is a common trap!). The sequences on the new DB might be lagging behind the actual max ID if they weren't synced perfectly.

-- Generate commands to fix sequences
SELECT 'SELECT setval(''' || c.relname || ''', max(' || a.attname || '));' 
FROM pg_class c 
JOIN pg_attribute a ON a.attrelid = c.oid 
WHERE c.relkind = 'S';

Legal & Compliance: The Norwegian Context

Operating in 2024 requires strict adherence to GDPR. The Schrems II ruling is old news, but its impact is still felt daily. If you are migrating user data, ensure your destination is not just fast, but compliant.

Hosting on US-owned cloud hyperscalers often introduces legal ambiguity regarding the CLOUD Act. By utilizing a provider like CoolVDS, where infrastructure is physically located in Oslo and owned by a local entity, you simplify your Record of Processing Activities (ROPA). Data stays in Norway. Latency stays low. Compliance officers stay happy.

Why IOPS Kill Migrations

I cannot stress this enough: CPU steal time and I/O wait are the enemies. In a shared hosting environment, your "neighbor" running a crypto miner will tank your import speed. You will see `rsync` speeds drop to KB/s.

CoolVDS allocates dedicated resources. When we benchamarked a 50GB restore on their NVMe tier, we sustained 450MB/s write speeds. On a standard generic VPS, we averaged 80MB/s. That is the difference between a 2-minute outage and a 15-minute outage.

Conclusion

Database migrations expose the weak points in your infrastructure. They punish slow disks, shaky networks, and lazy configurations. By using logical replication, you decouple the data transfer time from the downtime window.

Do not let your infrastructure be the bottleneck. Ensure your destination server has the IOPS to handle the write load and the network stability to maintain the replication stream.

Ready to test your migration plan? Deploy a high-performance KVM instance in Oslo on CoolVDS today and see what genuine NVMe throughput feels like.