A lightweight Bash tool to create, connect, and manage AWS EC2 development environments directly from your Mac.
Perfect for Cursor, VS Code Remote-SSH, or any cloud-based development workflow.
This script automates EC2 creation, locale configuration, and connectivity (via Mosh + SSH) β letting you spin up a full dev server in seconds and tear it down when done.
ββββββββββββββββββββββββββββββββ
β Your Mac β
β (Cursor / VS Code / CLI) β
ββββββββββββββββ¬ββββββββββββββββ
β SSH + Mosh
βΌ
ββββββββββββββββββββββββββββββββ
β EC2 Dev Instance β
β Ubuntu 24.04 (x86/ARM) β
β ββ Python + uv β
β ββ Node 20 LTS β
β ββ Claude Code (AI agent) β
β ββ tmux / mosh / git / htop β
ββββββββββββββββ¬ββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββ
β AWS Cloud β
β EC2 + Elastic IP + SG rules β
ββββββββββββββββββββββββββββββββ
Your Mac runs the control script (ec2-dev.sh) which:
- Creates and configures the EC2 instance
- Opens required ports (SSH 22, Mosh UDP 60000β61000)
- Bootstraps tools and UTF-8 locales
- Connects you via
mosh(auto-reconnects, fast typing latency)
Follow these steps once β after that, the script handles everything.
# Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# CLI tools
brew install awscli mosh jqAsk your IT manager to create an IAM user for you (e.g. dev-yourname) with EC2 permissions.
Theyβll give you:
- Access Key ID (starts with
AKIAβ¦) - Secret Access Key
Then configure AWS CLI:
aws configureEnter:
AWS Access Key ID [None]: AKIA...
AWS Secret Access Key [None]: your-secret
Default region name [None]: eu-west-1
Default output format [None]: json
Either import your local public key:
aws ec2 import-key-pair --region eu-west-1 --key-name mac-key --public-key-material fileb://$HOME/.ssh/id_ed25519.pubOr generate one from AWS:
aws ec2 create-key-pair --region eu-west-1 --key-name mac-key --query 'KeyMaterial' --output text > ~/.ssh/mac-key.pem
chmod 600 ~/.ssh/mac-key.pemaws sts get-caller-identity
aws ec2 describe-key-pairs --region eu-west-1# x86 (Intel) Free Tier
INSTANCE_TYPE=t3.micro ./ec2-dev.sh create
# or ARM (Graviton) Free Tier
ARCH=arm64 INSTANCE_TYPE=t4g.micro ./ec2-dev.sh create./ec2-dev.sh bootstrapThis installs:
mosh,tmux,htop,uv,git,node 20claude(Claude Code CLI)- Sets UTF-8 locales for Mosh
# Copy the example file
cp env.example .env
# Edit with your actual keys
nano .env
# Secure it
chmod 600 .env
# Transfer to remote instance
./ec2-dev.sh setup-secrets./ec2-dev.sh connectπ‘ Install Mosh locally:
brew install mosh
| Action | Command | Notes |
|---|---|---|
| Create instance | ./ec2-dev.sh create |
Auto-creates SG + optional Elastic IP |
| Check status | ./ec2-dev.sh status |
Shows ID, state, public IP |
| Connect (Mosh/SSH) | ./ec2-dev.sh connect |
Uses alias or current IP |
| Bootstrap | ./ec2-dev.sh bootstrap |
Installs mosh/tmux/uv/node/claude |
| Setup secrets | ./ec2-dev.sh setup-secrets |
Transfer API keys from .env (secure) |
| Stop instance | ./ec2-dev.sh stop |
Suspends compute billing |
| Start instance | ./ec2-dev.sh start |
Resumes a stopped VM |
| Terminate completely | ./ec2-dev.sh terminate |
Deletes instance, EIP, and SG |
| Allocate Elastic IP | ./ec2-dev.sh eip-allocate |
Creates stable IP |
| Associate Elastic IP | ./ec2-dev.sh eip-associate |
Attaches to instance |
| Release Elastic IP | ./ec2-dev.sh eip-release |
Frees unused IP |
| Get public IP | ./ec2-dev.sh ip |
Prints IP |
| Get instance ID | ./ec2-dev.sh id |
Prints instance ID |
When you're done testing (to avoid charges):
./ec2-dev.sh terminateThis single command will:
- Release the Elastic IP (if allocated)
- Terminate the EC2 instance
- Delete the security group
The instance will vanish from AWS after 1β2 minutes.
When ready for a faster dev box:
| Type | Arch | vCPU / RAM | Monthly (approx.) | Command |
|---|---|---|---|---|
| t4g.large | ARM | 2 / 8 GB | β¬45 | ARCH=arm64 INSTANCE_TYPE=t4g.large ./ec2-dev.sh create |
| m7i-flex.large | x86 | 2 / 8 GB | β¬75 | INSTANCE_TYPE=m7i-flex.large ./ec2-dev.sh create |
| m7i-flex.xlarge | x86 | 4 / 16 GB | β¬150 | INSTANCE_TYPE=m7i-flex.xlarge ./ec2-dev.sh create |
Then:
./ec2-dev.sh bootstrap
./ec2-dev.sh connectCursor works seamlessly with remote EC2 instances via SSH. Here's the complete guide to get you coding in the cloud.
# Create a dev instance (Free Tier or larger)
INSTANCE_TYPE=t3.micro ./ec2-dev.sh create
# Install essential development tools
./ec2-dev.sh bootstrap
# Check it's running
./ec2-dev.sh statusGet your instance's public IP:
./ec2-dev.sh ipCreate or edit your SSH config:
nano ~/.ssh/configAdd this entry (replace <YOUR_IP> with your actual IP or Elastic IP):
Host heyl
HostName <YOUR_IP>
User ubuntu
IdentityFile ~/.ssh/id_ed25519 # or ~/.ssh/mac-key.pem
ServerAliveInterval 60
ServerAliveCountMax 5
TCPKeepAlive yesπ‘ Pro Tip: Use an Elastic IP so this config never changes:
./ec2-dev.sh eip-allocate
./ec2-dev.sh eip-associatessh heylIf it connects successfully, type exit to return to your Mac.
π¦ First-Time Setup: When you connect for the first time, Cursor will prompt you to install the Cursor Remote - SSH extension. Click Install - this is required for remote development and is a one-time setup.
- Open Cursor
- Press
Cmd+Shift+P(Mac) orCtrl+Shift+P(Windows/Linux) - Type:
Remote-SSH: Connect to Host⦠- Select your host:
heyl - If prompted, install the Cursor Remote - SSH extension (first time only)
- Cursor will then:
- Install the Cursor Server on your EC2 instance (first time only)
- Connect your local Cursor UI to the remote environment
- Open a remote workspace
- Click the Remote Explorer icon in the left sidebar (looks like a computer monitor)
- Under "SSH Targets", find
heyl - Click the β (connect) icon
- Cursor connects and opens a new window
Once connected to your EC2 instance:
-
Open a remote folder:
FileβOpen Folderβ¦- Navigate to your project (e.g.,
/home/ubuntu/my-project) - Click OK
-
Clone a repository:
# In Cursor's integrated terminal cd ~ git clone https://github.com/yourname/your-repo.git cd your-repo
-
Reopen the folder:
FileβOpen Folderβ¦β Select the cloned repo
Even if your Cursor connection drops, tmux keeps your work running:
# Start a named session
tmux new -s dev
# Run long-running processes (servers, builds, etc.)
npm run dev
# or
uv run python main.py
# Detach: Ctrl+B, then D
# Reattach later: tmux attach -t devFor large projects, disable file watchers to save resources:
In Cursor settings (Cmd+,):
{
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/node_modules/**": true,
"**/.venv/**": true,
"**/dist/**": true,
"**/build/**": true
}
}To prevent losing work due to connection issues:
{
"files.autoSave": "afterDelay",
"files.autoSaveDelay": 1000
}Install extensions on the remote instance (not locally). Cursor will prompt you:
- Click Install in SSH: heyl when installing extensions
- Your extensions install directly on the EC2 instance
# Morning: Start your instance
./ec2-dev.sh start
# Connect with Cursor
# (Use Command Palette β Remote-SSH: Connect to Host β heyl)
# Inside Cursor's terminal:
tmux attach -t dev || tmux new -s dev
# Start your dev server
cd ~/my-project
npm run dev
# or
uv run uvicorn app.main:app --reload
# Code all day with Claude/AI assistance...
# Evening: Stop the instance to save costs
./ec2-dev.sh stop# Check instance is running
./ec2-dev.sh status
# Test SSH manually
ssh -v heyl
# If stopped, start it
./ec2-dev.sh start-
Verify security group allows SSH (port 22):
aws ec2 describe-security-groups \ --region eu-west-1 \ --filters "Name=group-name,Values=heyl-dev-sg" -
Check your IP didn't change (if not using Elastic IP):
./ec2-dev.sh ip # Update ~/.ssh/config with new IP
# SSH in and check disk space
ssh heyl
df -h
# If low on space, clean up:
sudo apt clean
docker system prune -af # if using Docker-
Upgrade instance type:
./ec2-dev.sh stop # Manually change instance type in AWS Console ./ec2-dev.sh start -
Or create a larger instance:
./ec2-dev.sh terminate INSTANCE_TYPE=t4g.large ./ec2-dev.sh create ./ec2-dev.sh bootstrap
Forward remote ports to your Mac (e.g., for web apps):
Add to ~/.ssh/config:
Host heyl
HostName <YOUR_IP>
User ubuntu
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 60
LocalForward 3000 localhost:3000 # Forward port 3000
LocalForward 8000 localhost:8000 # Forward port 8000Now http://localhost:3000 on your Mac shows your EC2 server!
Manage multiple projects with different configs:
# Project 1: Small dev box
EC2_NAME_TAG=project1 INSTANCE_TYPE=t3.micro ./ec2-dev.sh create
# Project 2: ML workload
EC2_NAME_TAG=project2 INSTANCE_TYPE=g4dn.xlarge ./ec2-dev.sh createAdd separate SSH configs for each:
Host project1
HostName <IP_1>
User ubuntu
Host project2
HostName <IP_2>
User ubuntuBoth work normally on remote instances! The AI features run on your Mac, but file access is over SSH.
-
Stop instances when not coding (evenings/weekends):
./ec2-dev.sh stop # Pays only for storage (~$0.08/day for 60GB) -
Use Free Tier for learning (750 hours/month):
INSTANCE_TYPE=t3.micro ./ec2-dev.sh create
-
Terminate test instances when done:
./ec2-dev.sh terminate
π‘ Quick Reference: Once set up, your daily workflow is simply:
./ec2-dev.sh startβ Open Cursor β Connect toheylβ Code β./ec2-dev.sh stop
Your EC2 instance comes with Claude Code pre-installed - an AI-powered coding agent that can help with development tasks.
After bootstrapping, you need to configure your Anthropic API key. Two secure methods:
Create a local .env file with your secrets (this file stays on your Mac):
# Create .env with your API keys
cat > .env <<EOF
ANTHROPIC_API_KEY=sk-ant-your-key-here
EOF
# Secure the file (important!)
chmod 600 .env
# Add to .gitignore (prevent accidental commits)
echo ".env" >> .gitignore
# Transfer secrets securely to your EC2 instance
./ec2-dev.sh setup-secretsThis command:
- β
Reads your local
.envfile - β Transfers only API keys via encrypted SSH
- β Never stores secrets in cloud-init or AWS metadata
- β
Automatically configures
~/.profileon the remote instance
# SSH to your instance
ssh ubuntu@$(./ec2-dev.sh ip)
# Set your API key (get one at https://console.anthropic.com/)
echo 'export ANTHROPIC_API_KEY=sk-ant-...' >> ~/.profile
source ~/.profile
# Verify installation
claude --version- Store API keys in local
.envwithchmod 600 - Add
.envto.gitignore - Use
setup-secretsto transfer keys securely - Keep your local
.envbacked up (encrypted)
- Never commit
.envto git - Never put API keys in cloud-init user-data
- Never store secrets in AWS EC2 metadata
- Don't share your
.envfile
The setup-secrets command safely transfers these environment variables:
ANTHROPIC_API_KEY(Claude Code)OPENAI_API_KEY(OpenAI APIs)GITHUB_TOKEN(GitHub API access)HUGGING_FACE_TOKEN(HuggingFace models)
Other variables in your .env are ignored for safety (like AWS credentials).
Claude Code can help with various development tasks:
# Get help
claude --help
# Ask Claude to create files
claude "Create a FastAPI hello world app in main.py"
# Ask Claude to debug code
claude "Why is my app.py throwing a 500 error?"
# Ask Claude to refactor
claude "Refactor database.py to use async/await"
# Ask Claude to write tests
claude "Write pytest tests for my user authentication module"You can use both tools together:
- Cursor AI: For interactive coding in your IDE with inline suggestions
- Claude Code CLI: For automated tasks, scripts, and batch operations
Example workflow:
# In terminal: Use Claude Code for setup
claude "Create a new FastAPI project with poetry"
# In Cursor IDE: Continue development with AI assistance
# Code with Cursor's inline AI help...
# In terminal: Use Claude Code for testing
claude "Run all tests and fix any failures"- Never commit your API key to git
- Set it in ~/.profile (already done by bootstrap)
- Monitor usage at https://console.anthropic.com/
- Consider a separate key for dev vs production
# One-time setup
INSTANCE_TYPE=t3.micro ./ec2-dev.sh create
./ec2-dev.sh bootstrap
# Create .env with your API keys (one-time)
echo 'ANTHROPIC_API_KEY=sk-ant-...' > .env
chmod 600 .env
echo '.env' >> .gitignore
# Transfer secrets to remote instance
./ec2-dev.sh setup-secrets
# Connect (via mosh)
./ec2-dev.sh connect
# Work inside tmux for persistent sessions
tmux new -s dev
# detach: Ctrl+B then D
# Stop when done
./ec2-dev.sh stop
# Restart next day
./ec2-dev.sh start
# Re-run setup-secrets if you rotated keys
./ec2-dev.sh setup-secrets
# Terminate when finished
./ec2-dev.sh terminate- macOS or Linux with:
- AWS CLI (
brew install awscli) - Homebrew (
brew install mosh jq) - SSH key pair (
mac-keyor equivalent)
- AWS CLI (
- IAM user with EC2 permissions
- AWS region set (default:
eu-west-1)
- Security group opens SSH (22) and Mosh UDP (60000β61000).
- Locale is pre-set to en_US.UTF-8 for Mosh compatibility.
- Cloud-init automatically installs essential dev tooling.
- Elastic IP is optional but recommended for stable connections.
- All instances are Ubuntu 24.04 LTS (Canonical official AMIs via SSM).
# Free Tier setup
INSTANCE_TYPE=t3.micro ./ec2-dev.sh create
./ec2-dev.sh bootstrap
# Configure secrets (one-time)
echo 'ANTHROPIC_API_KEY=sk-ant-...' > .env && chmod 600 .env
./ec2-dev.sh setup-secrets
# Connect and work
./ec2-dev.sh connect
# Work, test, build
# ...
# Stop when idle
./ec2-dev.sh stop
# Terminate after testing
./ec2-dev.sh terminateBuilt for rapid, clean, and reproducible EC2 dev environments.