Your end-of-month AWS bill makes you grind your teeth. €187/mo for a Node.js + Postgres 50 GB + 200 GB of static objects stack, of which 60% goes to RDS and NAT Gateway. You know there's something cheaper, but you hesitate to abandon the comfort of EC2 and the magic of S3. This guide is the honest write-up of a migration we ran internally in April 2026: from AWS to a single Contabo VPS at €9.99/month, 78% savings over 12 months, with no significant feature loss for our use case.
Why leave AWS in 2026
Three cumulative reasons, not one:
- Cost. AWS scales brutally with egress traffic ($0.09/GB above 100 GB), NAT Gateways ($45/mo just to exist), and RDS snapshots. A project that starts at $30/mo routinely ends up at $200-400/mo within 18 months without a single new feature.
- GDPR & sovereignty. Even in
eu-central-1(Frankfurt), AWS is a US entity bound by the CLOUD Act: a US federal judge can compel access to data stored in Europe. For EU customer data, that's a real legal risk, formalized by the EDPB's 2020 recommendations (post-Schrems II). - Technical lock-in. IAM, KMS, VPC, ALB, Lambda — every AWS service ties you in a bit more. After two years, "just moving" takes three weeks. The longer you wait, the more you pay, and the more expensive leaving becomes.
Contabo isn't magic: it's a low-end German VPS provider that hands you an Ubuntu box and lets you do the rest. But for 80% of SMB and solo SaaS workloads, it's more than enough — and 5 to 10× cheaper.
When migration pays off (and when it doesn't)
Be honest with yourself. AWS → Contabo makes sense if:
- You have one or two services to host (API + DB + objects), not a 30-container microservice architecture.
- Your traffic is predictable (no 100× baseline spikes). Contabo has no auto-scaling.
- You accept becoming the admin:
apt,systemctl,ufw, manual backups. - Your data doesn't tolerate the CLOUD Act, or your AWS bill is past €100/mo.
Conversely, stay on AWS if:
- You heavily use Lambda, Cognito, SageMaker, managed SQS. Migration cost > AWS cost.
- You have a customer SLA requiring 99.99% (Contabo is 99.9%, i.e. ~8h downtime/year).
- You handle tens of TB and your outbound traffic exceeds Contabo's quota (32 TB/mo).
Our case at VPNSmith: a single Node.js backend + Postgres + static objects. Every box checked. Decision made in an hour.
Audit current AWS cost (Cost Explorer method)
Before migrating, measure. Without a baseline, there's no ROI.
- Go to AWS Cost Explorer → Reports → Cost & Usage Reports.
- Filter the last 3 months, monthly granularity, grouped by Service.
- Export as CSV. Open in a spreadsheet. You typically get:
| AWS Service | Avg monthly cost |
|---|---|
| EC2 (1× t3.medium) | €32 |
| RDS (db.t3.small Postgres) | €38 |
| S3 (200 GB + requests) | €12 |
| Data Transfer Out | €24 |
| NAT Gateway | €41 |
| CloudFront | €18 |
| Route 53 + misc | €6 |
| Total | €171 / mo |
Annualized: €2,052. Note the detail: on this profile, NAT Gateway + Data Transfer = €65/mo, or 38% of the bill just to push bytes in and out. That's the classic blind spot.
Set up the target Contabo VPS
For this stack, we pick a Cloud VPS 10 from Contabo: 6 vCPU, 16 GB RAM, 400 GB NVMe, 1 Gbps, 32 TB traffic/month. €9.99/mo on a 24-month commitment.
Provisioning:
# Once the VPS is delivered (Contabo email with IP + root password)
ssh root@YOUR_IP
# Minimal hardening
adduser ericg
usermod -aG sudo ericg
mkdir -p /home/ericg/.ssh
nano /home/ericg/.ssh/authorized_keys # paste your SSH key
chmod 700 /home/ericg/.ssh && chmod 600 /home/ericg/.ssh/authorized_keys
chown -R ericg:ericg /home/ericg/.ssh
sed -i 's/^PermitRootLogin .*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart ssh
apt update && apt upgrade -y
apt install -y ufw fail2ban
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
systemctl enable --now fail2ban
Full hardening details (Lynis, Contabo snapshots, journald) are in the WireGuard self-host guide. Same foundations apply here.
Postgres RDS → self-hosted Postgres migration (pg_dump step by step)
This is the most critical piece. Battle-tested procedure:
Step 1 — Install Postgres 15 on Contabo
sudo apt install -y postgresql-15 postgresql-contrib-15
sudo systemctl enable --now postgresql
# Create app user and DB
sudo -u postgres psql <<EOF
CREATE USER appuser WITH PASSWORD 'PASTE_A_STRONG_PASSWORD';
CREATE DATABASE appdb OWNER appuser;
GRANT ALL PRIVILEGES ON DATABASE appdb TO appuser;
EOF
Enable SSL and allow remote connections only from your app (or local-only if Postgres runs on the same machine as the API — recommended):
sudo nano /etc/postgresql/15/main/postgresql.conf
# ssl = on
# listen_addresses = 'localhost' (or '*' if app lives on another VPS)
sudo nano /etc/postgresql/15/main/pg_hba.conf
# Add: hostssl appdb appuser YOUR_APP_IP/32 scram-sha-256
sudo systemctl restart postgresql
Step 2 — Dump from RDS
From your workstation, with RDS access:
pg_dump \
-h your-rds.eu-central-1.rds.amazonaws.com \
-U masteruser \
-d appdb \
-Fc \
-f appdb.dump
# Custom format (-Fc) = compressed + parallelizable on restore
For a 50 GB database, expect 10-25 min depending on RDS class and bandwidth.
Step 3 — Transfer and restore
scp appdb.dump ericg@YOUR_CONTABO_IP:/tmp/
ssh ericg@YOUR_CONTABO_IP
sudo -u postgres pg_restore \
-d appdb \
-j 4 \
--no-owner \
--role=appuser \
/tmp/appdb.dump
# Verify
sudo -u postgres psql -d appdb -c "SELECT count(*) FROM users;"
Check integrity by comparing count(*) of large tables between RDS and Contabo. Difference = 0.
Step 4 — Automated backups post-migration
You replace RDS automated backups with a simple cron:
sudo nano /usr/local/bin/pg-backup.sh
#!/bin/bash
DATE=$(date +%Y%m%d-%H%M)
sudo -u postgres pg_dump -Fc appdb > /var/backups/postgres/appdb-$DATE.dump
find /var/backups/postgres -name "appdb-*.dump" -mtime +14 -delete
# Push to MinIO or external S3 for off-site
rclone copy /var/backups/postgres/appdb-$DATE.dump remote:backups/postgres/
Daily cron at 3 AM: 0 3 * * * /usr/local/bin/pg-backup.sh.
S3 → MinIO migration (rclone, aws-cli compatibility)
MinIO is an open-source, Go-based object store that's 100% S3 API compatible. Direct install:
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
sudo mv minio /usr/local/bin/
sudo mkdir -p /data/minio
sudo useradd -r minio-user -s /sbin/nologin
sudo chown minio-user:minio-user /data/minio
sudo nano /etc/default/minio
MINIO_VOLUMES="/data/minio"
MINIO_OPTS="--console-address :9001 --address :9000"
MINIO_ROOT_USER=admin
MINIO_ROOT_PASSWORD=PASTE_A_STRONG_32_CHAR_PASSWORD
systemd unit: grab the official .service (MinIO docs) and systemctl enable --now minio.
Migrate objects from S3 using rclone:
sudo apt install -y rclone
rclone config
# n) New remote → name: aws / s3 / AWS / paste access_key + secret_key
# n) New remote → name: minio / s3 / Other / endpoint http://localhost:9000 / paste admin + password
# Bucket-to-bucket
rclone copy aws:my-prod-bucket minio:my-prod-bucket --transfers=8 --progress
For 200 GB: budget 2-5 hours depending on AWS egress bandwidth (which will cost you ~€18 in final transfer-out — that's the exit toll).
On the application side, no code change: your aws-sdk-js keeps working, you just point at the MinIO endpoint:
const s3 = new S3Client({
endpoint: 'https://storage.yourdomain.com',
region: 'us-east-1', // arbitrary required value
credentials: { accessKeyId, secretAccessKey },
forcePathStyle: true, // important for MinIO
});
Zero-downtime DNS cutover
The clean method, planned over 10 days:
- D-10: deploy the full stack on Contabo, run internal smoke-tests via
/etc/hostsoverride. - D-7: lower the TTL of A records in Route 53 from 3600 → 60 seconds. Let it propagate 48h.
- D-3: feature freeze, last incremental
pg_dump, lastrclone syncof objects. - D-1: cut over at off-peak time (typically 3 AM UTC for an EU audience). Change the A record: old AWS IP → new Contabo IP. Propagation max 60s.
- D+0 → D+2: aggressive monitoring (latency, 5xx errors, DB metrics). Keep AWS running "just in case".
- D+7: clean up AWS (terminate EC2, delete RDS after final snapshot exported, delete S3 bucket after off-site backup).
Zero perceptible user downtime if Postgres is sync'd (a 5 min read-only freeze is enough to ship the final delta).
Want a Contabo VPS to kick off your migration? Contabo Cloud VPS 10, 24-month term — €9.99/mo
Measured ROI (before/after cost table over 12 months)
Our internal migration, measured:
| Line item | AWS (before) | Contabo (after) |
|---|---|---|
| Compute (EC2 t3.medium / VPS Cloud 10) | €32 | €9.99 |
| Database (RDS / self-hosted Postgres) | €38 | €0 (included) |
| Object storage (S3 200 GB / MinIO) | €12 | €0 (included) |
| Data transfer out | €24 | €0 (32 TB/mo included) |
| NAT Gateway | €41 | €0 (not needed) |
| CloudFront CDN | €18 | €12 (external BunnyCDN) |
| DNS Route 53 | €6 | €0.40 (free Cloudflare + 1 domain) |
| Off-site backup (Wasabi 50 GB) | €0 | €3 |
| Monitoring (UptimeRobot pro) | €0 | €4 |
| Monthly total | €171 | €29.39 |
| Annual cost | €2,052 | €352.68 |
Saving: €1,699.32/yr, or 83%. On our real case, slightly heavier (€187/mo on AWS), that's 78% savings, or €1,752/yr. Payback is instant: no significant migration cost (one day of work).
What you lose vs AWS (managed services)
No marketing, let's be clear about the losses:
- RDS Multi-AZ auto-failover: on AWS, if the primary dies, the secondary takes over in 60-120s. Self-hosted on Contabo, you'd need a second VPS + streaming replication + Patroni. Doable but adds ops time.
- S3's 11 nines of durability: Amazon guarantees 99.999999999% durability per object (3-AZ internal replication). MinIO on a single disk = the durability of your NVMe disk. Fix: regular backups to Wasabi or Backblaze B2 (€2-6/mo for 200 GB off-site).
- Fine-grained IAM: replaced by Linux users + sudoers + MinIO policies. Less granular, simpler to manage.
- Unified CloudWatch: replace with Prometheus + Grafana, or Netdata (15 min setup), or an external SaaS (Better Stack, Sentry).
- Enterprise 24/7 support: Contabo replies by email within 24-48h. For critical ops, plan a runbook + a second admin on call.
Budget 3 to 5 hours of ops per month in steady state: Ubuntu updates, backup checks, monitoring alerts. That's the real hidden cost — not insurmountable, but real.
Read next
- Self-host VPN on Contabo: complete WireGuard guide 2026
- Cloud hosting GDPR: alternatives to AWS in Europe 2026
- Contabo vs Hetzner vs OVH: Europe VPS for self-host VPN 2026
- Official sources: MinIO docs, PostgreSQL streaming replication, AWS Cost Explorer guide, EDPB Schrems II recommendations
Article published 2026-06-02. ROI measured on VPNSmith's internal migration in April 2026. Affiliate disclosure: if you pick up a Contabo VPS via the links in this article, we earn a commission that funds the ongoing testing of our guides. The price you pay is identical. We only recommend what we run in production.
★ Datacenter Nuremberg GDPR · ✓ IPv4 dédiée incluse · 200+ Mbps garantis
Get Contabo30 jours satisfait ou remboursé→