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.

Tired of managing servers?

This site helps you find the right FOSS hosting solution. If you'd rather have experts handle the infrastructure, OpsHelp offers fully managed hosting that supports open source.