Overview

Migrating hosting providers is one of those tasks that looks simple (copy files, update DNS) but has hidden complexity. A poorly planned migration means downtime, data loss, or security gaps.

This guide walks through a complete, methodical migration from any proprietary platform (AWS, DigitalOcean, Linode, Vultr, etc.) to a FOSS-friendly VPS.

Time required: 2-4 hours depending on application complexity Downtime: Minimal (with proper blue-green approach)


Before You Start: Assessment

Inventory Your Current Infrastructure

Before touching anything, document what you have:

# SSH into your current server and document:
# 1. Installed packages
dpkg --get-selections | grep -v deinstall

# 2. Running services
systemctl list-units --type=service --state=running

# 3. Nginx/Apache config
ls -la /etc/nginx/sites-enabled/
cat /etc/nginx/sites-enabled/default

# 4. Databases
sudo -u postgres psql -l

# 5. Cron jobs
crontab -l
sudo cat /var/spool/cron/crontabs/root

# 6. Installed Node versions (if applicable)
node --version
npm list -g --depth=0

Calculate Resource Requirements

Check your actual usage, not what you think you’re using:

# CPU and RAM usage
htop
free -h

# Disk usage by directory
du -sh /var/*
du -sh /home/*

# Database sizes
sudo -u postgres psql -c "SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) FROM pg_database ORDER BY pg_database_size DESC;"

# Docker disk usage
docker system df

Step 1: Choose Your Target Platform

Based on the Best FOSS-Friendly VPS Hosts comparison:

Use CaseRecommended
General purpose, best valueHetzner
Tightest budgetNetcup
Maximum free resourcesOracle Free Tier
Privacy / public-interest project1984 Hosting

For this guide, we’ll assume migration to Hetzner (most common choice).

Create Your Hetzner Account

  1. Go to hetzner.com and create an account
  2. Verify with a phone number (SMS)
  3. Add SSH key for authentication:
    • In Hetzner Console → SSH Keys → Add
    • Paste your public key from ~/.ssh/id_ed25519.pub
  4. Order your VPS (CX21 recommended: 2 vCPU, 50 GB NVMe, 8 GB RAM for ~€6/mo)

Step 2: Prepare the New Server

Initial Setup

SSH into your new Hetzner server and run the VPS hardening guide first. This takes 30 minutes and is non-negotiable for production.

Install Required Software

# Update
sudo apt update && sudo apt upgrade -y

# Install core packages
sudo apt install -y \
    nginx \
    postgresql \
    redis-server \
    certbot \
    python3-certbot-nginx \
    ufw \
    htop \
    fail2ban \
    unattended-upgrades

# If using Docker:
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER

# If using Coolify:
curl -fsSL https://get.coolify.io | bash

Step 3: Migrate Data

Option A: Direct rsync (Best for Large Files)

# On the NEW server, create the target directory
sudo mkdir -p /var/www
sudo chown -R $USER:$USER /var/www

# On the OLD server, run rsync (from OLD server)
rsync -avz --progress \
    -e "ssh -i ~/.ssh/your_key" \
    /var/www/ \
    aaron@<new-server-ip>:/var/www/

Option B: Database Dump and Restore (PostgreSQL)

# On the OLD server — create dump
sudo -u postgres pg_dumpall > /tmp/postgres_backup.sql
gzip /tmp/postgres_backup.sql

# Transfer to new server
rsync -avz -e "ssh -i ~/.ssh/your_key" \
    /tmp/postgres_backup.sql.gz \
    aaron@<new-server-ip>:/tmp/

# On the NEW server — restore
gunzip /tmp/postgres_backup.sql.gz
sudo -u postgres psql -f /tmp/postgres_backup.sql

Option C: Use Docker Volumes

# On the OLD server — backup Docker volumes
docker run --rm \
    -v app_data:/data \
    -v $(pwd):/backup \
    alpine \
    tar czf /backup/app_data.tar.gz -C /data .

# Transfer and restore on new server
docker run --rm \
    -v app_data:/data \
    -v $(pwd):/backup \
    alpine \
    sh -c "rm -rf /data/* && tar xzf /backup/app_data.tar.gz -C /data"

Step 4: Configure the New Server

Nginx Configuration

sudo nano /etc/nginx/sites-available/yourapp
server {
    listen 80;
    server_name yourdomain.com;

    root /var/www/yourapp;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    # Proxy to application if needed
    location /api {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
# Enable the site
sudo ln -s /etc/nginx/sites-available/yourapp /etc/nginx/sites-enabled/
sudo nginx -t  # Test config
sudo systemctl reload nginx

SSL Certificate

sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Step 5: DNS Cutover Strategy

Never change DNS without a rollback plan.

Blue-Green Migration Approach

  1. Before migration: Note your current DNS TTL (usually 300-3600 seconds)
  2. Reduce TTL to 300 (5 minutes) on the old provider — do this 24 hours before migration
  3. Keep the old server running until DNS propagates
  4. Update DNS A record to new server IP
  5. Wait 15-30 minutes for propagation
  6. Monitor — check logs, error tracking, user reports
  7. Only shut down old server after 48 hours of confirmed successful operation
# Check current TTL
dig +short SOA yourdomain.com | awk '{print $2}'

# Check DNS propagation from multiple locations
dig @8.8.8.8 yourdomain.com A
dig @1.1.1.1 yourdomain.com A

Step 6: Validate Everything

Before declaring success, verify:

Functionality

Security

Performance

Backups


Step 7: Post-Migration Cleanup

After 48 hours of confirmed success:

# On the OLD server, take a final backup
# Then decommission:
# 1. Disable services
sudo systemctl stop nginx postgresql

# 2. Document any config you want to keep
# 3. Terminate the instance via the provider's console

# On the NEW server, update DNS TTL back to reasonable value (3600)

Common Migration Pitfalls

”It Works on My Machine”

Your new server might have different software versions. Always check:

# Compare versions
node --version
nginx -v
psql --version

Environment Variables

Never copy .env files directly. Instead, manually recreate them on the new server:

# On old server, view env vars (without values if possible)
printenv | grep -v PATH

Permissions

Web servers run as www-data or similar. Verify file permissions:

# Set correct ownership
sudo chown -R www-data:www-data /var/www/yourapp

# Set correct permissions
sudo find /var/www/yourapp -type d -exec chmod 755 {} \;
sudo find /var/www/yourapp -type f -exec chmod 644 {} \;

Missing Dependencies

Your application might need system libraries not present on the new server:

# Check what libraries your binary/app needs
ldd /path/to/your/binary

Rollback Plan

If the migration fails:

  1. Do not delete the old server until everything is validated
  2. Update DNS back to old server IP immediately
  3. Investigate in a new debug session, not under pressure
  4. Document what went wrong for next time

Cost Comparison Example

ProviderPlanMonthly Cost
AWSt3.medium + EBS + bandwidth~$40-60
DigitalOcean2GB + volumes~$12
HetznerCX21€6

Migrating from AWS to Hetzner can save €400-600+ per year for equivalent workloads.

Need help moving a live service?

Keep control of your accounts and software. OpsHelp can help check backups, map dependencies and plan the move and rollback with you.