Skip to content

Instantly share code, notes, and snippets.

@alexeygrigorev
Created December 28, 2025 13:10
Show Gist options
  • Select an option

  • Save alexeygrigorev/608f6b20197f52044fc4a4d92d9cb1d8 to your computer and use it in GitHub Desktop.

Select an option

Save alexeygrigorev/608f6b20197f52044fc4a4d92d9cb1d8 to your computer and use it in GitHub Desktop.
Remove claude code

AWS Claude Web Instance Setup

Automated setup of a cheap AWS EC2 instance running Claude Code with a web UI.

Prompt:

create the cheapest instance on aws with 8gb of ram (arm is okay) and use ssh (razer.pem) to get there and install claude there, copy the same config as you use now and make claude available via web

Instance Details

Property Value
Instance Type t4g.medium (4GB RAM, ARM64)
AMI Ubuntu 22.04 LTS (ARM64)
Region eu-west-1

Access

Claude Web UI

http://public-dsn:8080

Web Terminal (ttyd)

http://public-dsn:8081

SSH

ssh -i ~/.ssh/razer.pem ubuntu@public-dsn

What Was Installed

  1. Node.js v24 - JavaScript runtime
  2. Claude Code CLI - @anthropic-ai/claude-code (v2.0.76)
  3. Claude Code UI - Web interface by siteboon (v1.12.0) on port 8080
  4. ttyd - Web terminal (v1.7.7) on port 8081 with password auth
  5. Build tools - cmake, gcc, etc. for building dependencies

Configuration

Use the same config (location: ~/.claude/settings.json) on the instance.

Service Management

Both services run as systemd:

# Claude Web UI (port 8080)
sudo systemctl status claude-web
sudo systemctl restart claude-web
journalctl -u claude-web -f

# Web Terminal (port 8081)
sudo systemctl status ttyd
sudo systemctl restart ttyd
journalctl -u ttyd -f

Starting the Instance

When you stop the instance, the public IP changes on restart. To start and get the new IP:

# Start the instance
aws ec2 start-instances --instance-ids i-0856daa3bc7c14cb5

# Wait for it to be running, then get the new public IP
aws ec2 describe-instances --instance-ids i-0856daa3bc7c14cb5 \
  --query "Reservations[0].Instances[0].PublicIpAddress" --output text

Then update secret.md with the new IP and access via:

  • Claude Web UI: http://<NEW-IP>:8080
  • Web Terminal: http://<NEW-IP>:8081
  • SSH: ssh -i ~/.ssh/razer.pem ubuntu@<NEW-IP>

Instance Control

# Stop instance (stops billing)
aws ec2 stop-instances --instance-ids i-0856daa3bc7c14cb5

# Start instance
aws ec2 start-instances --instance-ids i-0856daa3bc7c14cb5

# Terminate (delete instance)
aws ec2 terminate-instances --instance-ids i-0856daa3bc7c14cb5

# Check instance state
aws ec2 describe-instances --instance-ids i-0856daa3bc7c14cb5 \
  --query "Reservations[0].Instances[0].State.Name" --output text

Security Group

Security Group: claude-web-sg (sg-0b5dcc5f6e6674dc7)

Port Service Access
22 SSH 0.0.0.0/0
8080 Claude Web UI 0.0.0.0/0
8081 Web Terminal (ttyd) 0.0.0.0/0 (password protected)

Cost Estimate

  • On-demand: ~$15-20/month (t4g.medium in eu-west-1)
  • Spot: ~$5-8/month (if switched to spot instances)

Setup Commands (Reference)

# 1. Find latest Ubuntu ARM64 AMI
aws ec2 describe-images \
  --owners 099720109477 \
  --filters "Name=name,Values=ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-arm64-server-*" "Name=state,Values=available" \
  --query "sort_by(Images, &CreationDate)[-1].ImageId" \
  --output text

# 2. Create security group
aws ec2 create-security-group --group-name claude-web-sg --description "Security group for Claude web access"

# 3. Add rules
aws ec2 authorize-security-group-ingress --group-name claude-web-sg --protocol tcp --port 22 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-name claude-web-sg --protocol tcp --port 8080 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-name claude-web-sg --protocol tcp --port 8081 --cidr 0.0.0.0/0

# 4. Launch instance
aws ec2 run-instances \
  --image-id ami-01b277d8dbe41e3de \
  --instance-type t4g.medium \
  --key-name razer \
  --security-groups claude-web-sg \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=claude-web}]'

# 5. On instance (after SSH):
# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -S bash -
sudo apt-get install -y nodejs

# Install Claude Code
sudo npm install -g @anthropic-ai/claude-code

# Clone and setup web UI
git clone https://github.com/siteboon/claudecodeui.git
cd claudecodeui
npm install
npm run build

# Create .env
cat > .env << EOF
ANTHROPIC_AUTH_TOKEN=<token>
ANTHROPIC_BASE_URL=https://api.z.ai/api/anthropic
HOST=0.0.0.0
PORT=8080
EOF

# Create systemd service
sudo tee /etc/systemd/system/claude-web.service << EOF
[Unit]
Description=Claude Web UI
After=network.target

[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/claudecodeui
ExecStart=/usr/bin/node server/index.js
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable claude-web
sudo systemctl start claude-web

# Install ttyd (web terminal)
sudo apt-get install -y build-essential cmake git libjson-c-dev libwebsockets-dev
cd /tmp
git clone https://github.com/tsl0922/ttyd.git
cd ttyd
mkdir build && cd build
cmake ..
make -j$(nproc)
sudo make install

# Create ttyd systemd service
sudo tee /etc/systemd/system/ttyd.service << EOF
[Unit]
Description=Ttyd Web Terminal
After=network.target

[Service]
Type=simple
User=ubuntu
ExecStart=/usr/local/bin/ttyd -p 8081 -i 0.0.0.0 -c admin:claude123 -W bash
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable ttyd
sudo systemctl start ttyd
{
"permissions": {
"allow": [
"Bash(aws ec2:*)",
"Bash(aws configure:*)",
"Bash(aws ec2 run-instances:*)",
"Bash(chmod:*)",
"Bash(ssh:*)",
"Bash(ls:*)"
]
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment