Why Migrate from AWS EC2?
AWS EC2 costs can escalate quickly, especially with data transfer charges, EBS storage, and associated services like RDS, ElastiCache, and ALB. A self-managed VPS can provide equivalent or better performance at a fraction of the cost. This guide covers a systematic migration from EC2 to a VPS.
Cost Comparison
A typical t3.medium EC2 instance (2 vCPU, 4GB RAM) costs approximately $30-40/month before adding EBS, bandwidth, and other AWS services. An equivalent VPS typically costs $10-20/month with generous bandwidth included. For many workloads, the savings are 60-80%.
Pre-Migration Assessment
- Inventory all AWS services in use (EC2, RDS, S3, SES, CloudFront, Route 53)
- Document security groups and network ACLs
- List all environment variables and secrets
- Map dependencies between services
- Identify AWS-specific services that need alternatives
AWS Service Alternatives
| AWS Service | Self-Hosted Alternative |
|---|---|
| RDS | MySQL/PostgreSQL installed directly |
| ElastiCache | Redis installed directly |
| S3 | MinIO or Backblaze B2 |
| SES | Postal, Postfix, or external SMTP (Postmark/Resend) |
| CloudFront | Cloudflare (free tier) |
| Route 53 | Cloudflare DNS (free) |
| ALB | Nginx reverse proxy |
| CloudWatch | Prometheus + Grafana |
Step 1: Prepare the VPS
# Provision a VPS with matching or better specs
# Install the same OS version as your EC2 instance
# Install required software
sudo apt update && sudo apt upgrade -y
sudo apt install -y nginx mysql-server redis-server \
php8.3-fpm php8.3-mysql php8.3-redis php8.3-curl \
certbot python3-certbot-nginx ufw fail2ban
# Configure firewall
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
# Harden SSH
sudo sed -i "s/PermitRootLogin yes/PermitRootLogin no/" /etc/ssh/sshd_config
sudo systemctl restart sshd
Step 2: Migrate the Database
# From RDS or EC2 MySQL:
# On EC2/RDS, export the database
mysqldump -h rds-endpoint.amazonaws.com -u admin -p \
--single-transaction --routines --triggers \
--databases myapp_db | gzip > myapp_db.sql.gz
# Transfer to VPS
scp myapp_db.sql.gz user@vps-ip:/tmp/
# On VPS, import
zcat /tmp/myapp_db.sql.gz | mysql -u root
# Create application database user
mysql -e "CREATE USER 'myapp'@'localhost' IDENTIFIED BY 'secure_password';
GRANT ALL PRIVILEGES ON myapp_db.* TO 'myapp'@'localhost';
FLUSH PRIVILEGES;"
Step 3: Transfer Application Files
# From EC2 to VPS using rsync
rsync -avz --progress -e "ssh -i ~/.ssh/ec2-key.pem" \
ec2-user@ec2-ip:/var/www/myapp/ \
user@vps-ip:/var/www/myapp/
# Or use scp for a one-time transfer
scp -r -i ~/.ssh/ec2-key.pem \
ec2-user@ec2-ip:/var/www/myapp/ /var/www/myapp/
# Fix permissions
sudo chown -R www-data:www-data /var/www/myapp
sudo chmod -R 755 /var/www/myapp
Step 4: Configure Nginx
# Create Nginx virtual host
sudo tee /etc/nginx/sites-available/myapp << NGINX
server {
listen 80;
server_name example.com www.example.com;
root /var/www/myapp/public;
index index.php index.html;
location / {
try_files \$uri \$uri/ /index.php?\$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
include fastcgi_params;
}
}
NGINX
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
# Get SSL certificate
sudo certbot --nginx -d example.com -d www.example.com
Step 5: Update Application Configuration
# Update .env or configuration files
DB_HOST=localhost # Was RDS endpoint
DB_DATABASE=myapp_db
DB_USERNAME=myapp
DB_PASSWORD=secure_password
REDIS_HOST=127.0.0.1 # Was ElastiCache endpoint
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis
# Replace S3 references with MinIO or local storage
FILESYSTEM_DISK=local
# Or set up MinIO for S3-compatible local storage
Step 6: DNS Cutover
# 1. Lower DNS TTL to 300 seconds (24h before migration)
# 2. Test everything on VPS using hosts file
# 3. When ready, update DNS A record to VPS IP
# 4. Monitor for issues during propagation
# 5. Keep EC2 running for 48h as fallback
Post-Migration Checklist
- Verify all application features work correctly
- Set up automated backups (Restic, Borg, or similar)
- Configure monitoring (Uptime Kuma, Prometheus)
- Set up log rotation for Nginx and application logs
- Terminate EC2 instances and related AWS services after confirmation period
- Cancel or downgrade AWS account to avoid residual charges