AboutBlogContact
DevOps & InfrastructureJanuary 30, 2013 6 min read 107Updated: June 22, 2026

The Cloud Migration (2010-2013): Moving from Physical Servers to Early AWS and Heroku

AunimedaAunimeda
📋 Table of Contents

In 2010 we had a dedicated server in a colocation datacenter. A 1U rack-mount machine, dual Xeon processors, 16GB RAM, RAID-1 storage array. It cost $400/month for the rack space plus the upfront hardware cost of around $3,500. Provisioning a new server meant ordering hardware, waiting for delivery, physically installing it (or paying the datacenter to do it), and installing the OS over KVM.

New project? Same server. It ran Apache, MySQL, PHP, and a growing collection of client sites that had no business sharing the same machine. Deploying meant SSH-ing in and copying files. "Staging environment" meant a subdirectory.

Amazon Web Services launched EC2 in August 2006. We ignored it for years - the pricing was confusing, the tooling was primitive, and "the cloud" sounded like a marketing term for someone else's computer. By 2010 we started paying attention. By 2013 we were committed.


The EC2 Learning Curve

AWS in 2010 looked nothing like AWS today. The management console was rudimentary. CloudFormation didn't exist. Auto Scaling existed but was complex to configure. The primary tool was the AWS CLI and hand-crafted shell scripts.

Our first EC2 instance (September 2010):

# AWS CLI v1 (Ruby-based, before Python rewrite)
# Launch a t1.micro instance with Ubuntu AMI
ec2-run-instances ami-6936fb00 \
  --instance-type t1.micro \
  --key-name aunimeda-key \
  --group web-sg \
  --region us-east-1

# Check it launched
ec2-describe-instances

# Allocate and associate an Elastic IP (static IP)
ec2-allocate-address
ec2-associate-address -i i-12345678 54.23.45.67

# SSH in
ssh -i ~/.ssh/aunimeda-key.pem ubuntu@54.23.45.67

Then the familiar Linux setup: apt-get install apache2 mysql-server php5... exactly the same as a physical machine, but without the hardware delivery wait. The first time we launched a server in 90 seconds rather than 3 weeks, the business model changed.

The pricing revelation. A t1.micro was $0.02/hour in 2010. $14.40/month. For a development environment that we only needed during business hours, we could stop it evenings and weekends: 10 hours/day × 5 days/week × 4.3 weeks = ~215 hours/month × $0.02 = $4.30/month. Our $400/month dedicated server was running 24/7 serving sites that got 500 visitors/day. The economics were wrong.


The S3 Revelation

Amazon S3 (Simple Storage Service) launched in March 2006. We started using it seriously in 2011.

Before S3: user-uploaded files went to the web server's filesystem. Backups were rsync to another server. If the server filled up, you bought a bigger disk (not possible in EC2 without effort) or cleaned up old files. If the server died, files were potentially lost.

// Old approach: files on the web server filesystem
$uploadDir = '/var/www/html/uploads/';
$filename = time() . '_' . $_FILES['image']['name'];
$filePath = $uploadDir . $filename;

if (move_uploaded_file($_FILES['image']['tmp_name'], $filePath)) {
    $imageUrl = 'https://yoursite.com/uploads/' . $filename;
    saveToDatabase($imageUrl);
}
// New approach: files on S3
require 'vendor/autoload.php';
use Aws\S3\S3Client;

$s3 = S3Client::factory([
    'key'    => 'YOUR_AWS_KEY',
    'secret' => 'YOUR_AWS_SECRET',
    'region' => 'us-east-1',
]);

$filename = time() . '_' . basename($_FILES['image']['name']);
$bucket = 'aunimeda-uploads';

try {
    $result = $s3->putObject([
        'Bucket'      => $bucket,
        'Key'         => 'images/' . $filename,
        'SourceFile'  => $_FILES['image']['tmp_name'],
        'ContentType' => $_FILES['image']['type'],
        'ACL'         => 'public-read',  // Publicly accessible
    ]);
    
    $imageUrl = $result['ObjectURL'];  // https://s3.amazonaws.com/bucket/key
    saveToDatabase($imageUrl);
    
} catch (Aws\S3\Exception\S3Exception $e) {
    error_log('S3 upload failed: ' . $e->getMessage());
    // Fallback to local upload
}

The consequences: uploads survived server replacements. Storage scaled without capacity planning. S3's durability guarantee (99.999999999% - eleven 9s) was vastly better than a single-disk RAID array. And the cost was $0.023/GB/month - essentially free for most use cases.


Heroku: Zero-Infrastructure Deployment

Heroku launched public beta in 2009, full launch in 2011. It abstracted away all infrastructure: no SSH, no server configuration, no Apache setup. Just push code.

# The Heroku workflow (2011)
heroku create aunimeda-app

# Configure environment variables (no .env files in production)
heroku config:set DATABASE_URL=postgres://...
heroku config:set SECRET_KEY=randomstring
heroku config:set S3_BUCKET=aunimeda-production

# Deploy: git push = deploy
git push heroku main

# Scale
heroku ps:scale web=2  # Two web dynos
heroku ps:scale worker=1  # One background worker

# Logs
heroku logs --tail

# Database (Heroku Postgres)
heroku addons:create heroku-postgresql:dev
heroku pg:info
heroku pg:psql  # Direct database console

The Procfile defined what ran:

# Procfile
web: gunicorn app:app
worker: python worker.py

For a startup with no ops engineers, Heroku was transformative. A solo developer could deploy a production Rails or Python app with a Postgres database, a Redis cache, and background workers in 30 minutes. No SysAdmin degree required.

The cost: Heroku's dynos were expensive at scale compared to raw EC2. A 1X dyno ($25/month) had 512MB RAM. A small AWS t2.small ($17/month) had 2GB RAM. At scale, the infrastructure cost difference was significant.

The tradeoff we found: Heroku for early-stage projects (under $2000/month infrastructure cost), AWS for mature products (where ops investment was justified by savings).


The Auto-Scaling Moment

The most powerful AWS feature wasn't EC2 or S3. It was Auto Scaling Groups - servers that scaled up under load and back down when traffic dropped.

Our first real scaling event happened during a client's TV advertisement. A regional retailer ran a 30-second ad at 9pm prime time. Their product page got 2,400 concurrent visitors in the 5 minutes after the ad aired, versus a normal load of 40 concurrent users.

Without auto-scaling: server would have gone down.

With Auto Scaling:

# Auto Scaling Group configuration (2012 CLI)
as-create-auto-scaling-group aunimeda-web-asg \
  --launch-configuration web-server-lc \
  --min-size 1 \
  --max-size 10 \
  --desired-capacity 2 \
  --availability-zones us-east-1a us-east-1b \
  --load-balancers aunimeda-elb

# Scale-up policy: add 2 servers when CPU > 70% for 3 minutes
as-put-scaling-policy scale-up \
  --auto-scaling-group aunimeda-web-asg \
  --adjustment-type ChangeInCapacity \
  --scaling-adjustment 2 \
  --cooldown 300

# CloudWatch alarm triggers scale-up
mon-put-metric-alarm high-cpu \
  --alarm-actions arn:aws:autoscaling:...:scalingPolicy:...scale-up \
  --metric-name CPUUtilization \
  --namespace AWS/EC2 \
  --statistic Average \
  --period 180 \
  --threshold 70 \
  --comparison-operator GreaterThanOrEqualToThreshold \
  --dimensions AutoScalingGroupName=aunimeda-web-asg

During the TV ad event, 6 servers spun up within 4 minutes. Traffic handled. Ad over, servers terminated 30 minutes later. Total extra cost: ~$0.40. If we'd provisioned for peak traffic on dedicated hardware, we'd have had 6 servers running idle 23.5 hours per day.


The Shift in How We Thought

The most fundamental change from physical to cloud wasn't technical. It was psychological.

Physical servers were pets: named, carefully maintained, upgraded individually, mourned when they died. Cloud servers were cattle: numbered, replaceable, terminated without ceremony when they misbehaved.

This shift enabled automated deployments without fear: if a deployment broke a server, terminate it and launch a new one from a known-good AMI. It enabled blue-green deployments: launch new servers with new code, route traffic to them, terminate old servers. No maintenance windows. No SSH-in-and-patch-carefully.

The cloud migration wasn't just moving infrastructure to someone else's hardware. It was changing the relationship between software developers and operations - the beginning of what would be called DevOps.


Aunimeda provides DevOps engineering and infrastructure services - CI/CD pipelines, containerization, cloud deployments, and monitoring setups.

Contact us to discuss your infrastructure needs. See also: DevOps Services, Custom Software Development

Read Also

Docker Compose vs Kubernetes: What Small Teams Actually Need in 2026aunimeda
DevOps & Infrastructure

Docker Compose vs Kubernetes: What Small Teams Actually Need in 2026

Kubernetes is powerful and over-engineered for most small products. Docker Compose is simple and hits its limits faster than you'd think. Here's where the actual boundary is, with real configs for both.

DevOps for Startups - What You Actually Need (And What to Skip)aunimeda
DevOps & Infrastructure

DevOps for Startups - What You Actually Need (And What to Skip)

Most startup DevOps guides tell you to set up Kubernetes. You don't need Kubernetes. Here's the minimal, effective DevOps setup for a product with under 100k users.

Cloud Hosting Comparison 2026: AWS vs GCP vs Azure vs Hetzner vs Vercelaunimeda
DevOps & Infrastructure

Cloud Hosting Comparison 2026: AWS vs GCP vs Azure vs Hetzner vs Vercel

Which cloud provider to choose for your startup in 2026. Real pricing comparison, performance benchmarks, and the hosting stack that makes sense at each stage.

Need IT development for your business?

We build websites, mobile apps and AI solutions. Free consultation.

DevOps Services

Get Consultation All articles