How to Deploy Remote Development Environment with VS Code Server and Amazon Q Developer CLI
Deploy a cloud-based VS Code Server that comes with the pre-installed Amazon Q Developer CLI, allowing full-stack development from any device, including an iPad.
Problem Statement
Modern developers face a critical challenge: their development environments are anchored to specific machines. A developer who starts work on their office computer cannot seamlessly continue on their iPad while traveling without complex setup and synchronization issues.
This creates three major problems: device mobility limitations, environment inconsistencies across team members leading to "works on my machine" issues, and the difficulty of accessing AI-powered tools like Amazon Q consistently across different devices. Developers are forced to choose between mobility and productivity.
Architecture Solution
Cloud-Based Development Environment
This solution creates a cloud-based development environment using AWS infrastructure that provides consistent, accessible development capabilities from any device with a web browser.
Running Q Dev CLI on VS Code Server on an iPad
This is how it works: When a developer accesses the environment, their browser connects to an EC2 instance running VS Code Server on port 8080. Nginx acts as a reverse proxy, forwarding requests to the VS Code Server while adding security headers. The Amazon Q CLI is pre-installed and accessible through the integrated terminal, providing AI assistance for development tasks without any local setup requirements.
Key Components:
- EC2 Instance: Hosts VS Code Server and development tools
- VS Code Server: Provides browser-based IDE functionality
- Amazon Q CLI: Offers AI-powered development assistance
- Nginx: Handles reverse proxy and security headers
Implementation Details
The solution uses Infrastructure as Code (CloudFormation) to deploy a complete development environment with automated configuration:
VS Code Server Configuration:
# Password-protected web IDE bind-addr: 127.0.0.1:8080 auth: password password: $GENERATED_PASSWORD cert: false
Amazon Q CLI Installation:
# Installs all Q CLI binaries for full functionality curl --proto "=https" --tlsv1.2 -sSf \ "https://desktop-release.q.us-east-1.amazonaws.com/latest/q-x86_64-linux.zip" -o "q.zip" unzip q.zip cp q/bin/* ~/.local/bin/ # Copies q, qchat, qterm chmod +x ~/.local/bin/*
Multi-Region Support: The template uses AWS Systems Manager Parameter Store to automatically select the correct Ubuntu AMI for any AWS region, ensuring deployment works across regions without modification.
Deployment Process
Save the CloudFormation template from the Appendix section and deploy using AWS CLI:
# Save the template from Appendix as a file # Copy the CloudFormation template from the Appendix section below # Save it as: code-server-q-dev.yaml # Deploy the CloudFormation stack aws cloudformation create-stack \ --stack-name remote-dev-vscode-server \ --template-body file://code-server-q-dev.yaml \ --parameters \ ParameterKey=ProjectName,ParameterValue=remote-dev \ ParameterKey=InstanceType,ParameterValue=t3.small \ ParameterKey=VolumeSize,ParameterValue=10 \ --capabilities CAPABILITY_NAMED_IAM \ --region us-east-1 # Wait for deployment completion aws cloudformation wait stack-create-complete \ --stack-name remote-dev-vscode-server \ --region us-east-1
Get Access Information:
# Retrieve the access URL aws cloudformation describe-stacks \ --stack-name remote-dev-vscode-server \ --region us-east-1 \ --query 'Stacks[0].Outputs[?OutputKey==`VSCodeServerUrl`].OutputValue' \ --output text
Testing and Validation
Step 1: Verify Deployment Status
Check that all CloudFormation resources are created successfully:
aws cloudformation describe-stacks \ --stack-name remote-dev-vscode-server \ --region us-east-1 \ --query 'Stacks[0].StackStatus'
CloudFormation Deployment Output via AWS Console
Expected result: "CREATE_COMPLETE"
Step 2: Retrieve Access Credentials
Get the generated password from EC2 console logs:
- Navigate to EC2 Console → Instances
- Select your VS Code Server instance
- Actions → Monitor and troubleshoot → Get system log
- Search for "VS CODE SERVER PASSWORD"
- Copy the generated password
Step 3: Access the Development Environment
- Open web browser on any device (iPad, laptop, desktop)
- Navigate to the provided access URL
- Enter the retrieved password
- Verify VS Code Server loads successfully
VS Code Server login screen showing password authentication
Step 4: Test Amazon Q CLI Integration
Open the integrated terminal in VS Code Server and run:
# Verify Q CLI installation q --version # Login to Amazon Q using device flow q login --use-device-flow # Test chat functionality after login q chat
Note: Amazon Q offers both Q Builder (free tier) with basic AI assistance and Q Developer Pro with advanced features. You can start with the free tier and upgrade to Pro License for enhanced capabilities like advanced code generation and enterprise features.
Expected results:
- Version information displays correctly
- Device flow login completes successfully
- Chat interface launches without errors
- AI assistance responds to development queries
Amazon Q CLI working in VS Code Server terminal
Security Considerations
This solution implements basic security measures: security groups restrict access to HTTP only, SSH is disabled, and data uses encrypted storage.
The main limitation is HTTP-only traffic, which transmits data in plaintext.
For production use, consider adding HTTPS certificates to meet enterprise security requirements.
Conclusion
This solution successfully addresses remote development challenges by providing universal device access, consistent development environment with AI assistance, and rapid deployment capabilities. The cost-effective operation at approximately $13-14 per month transforms any device into a powerful development workstation.
Appendix: Complete Infrastructure Code
CloudFormation Template (code-server-q-dev.yaml):
AWSTemplateFormatVersion: '2010-09-09' Description: 'VS Code Server - Professional Minimal Template (No SSH) for Multi-Region Support' Parameters: ProjectName: Type: String Default: 'vscode-server' Description: 'Name of the project (used for resource naming)' InstanceType: Type: String Default: 't3.small' AllowedValues: - 't3.nano' - 't3.micro' - 't3.small' - 't3.medium' - 't3.large' Description: 'EC2 instance type (t3.small recommended for cost efficiency)' VolumeSize: Type: Number Default: 10 MinValue: 8 MaxValue: 50 Description: 'EBS volume size in GB (10GB minimum for cost efficiency)' Resources: # VPC - Minimal setup VPC: Type: AWS::EC2::VPC Properties: CidrBlock: '10.0.0.0/16' EnableDnsHostnames: true EnableDnsSupport: true Tags: - Key: Name Value: !Sub '${ProjectName}-vpc' # Internet Gateway InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub '${ProjectName}-igw' InternetGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC # Single Public Subnet PublicSubnet: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [0, !GetAZs ''] CidrBlock: '10.0.1.0/24' MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub '${ProjectName}-public-subnet' # Route Table PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub '${ProjectName}-public-rt' DefaultPublicRoute: Type: AWS::EC2::Route DependsOn: InternetGatewayAttachment Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway PublicSubnetRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet # Security Group - HTTP only (no SSH) VSCodeSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: !Sub '${ProjectName}-sg' GroupDescription: 'Security group for VS Code Server (HTTP only)' VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: '0.0.0.0/0' Description: 'HTTP for VS Code Server' SecurityGroupEgress: - IpProtocol: -1 CidrIp: '0.0.0.0/0' Description: 'All outbound traffic' Tags: - Key: Name Value: !Sub '${ProjectName}-sg' # IAM Role for Systems Manager EC2Role: Type: AWS::IAM::Role Properties: RoleName: !Sub '${ProjectName}-ec2-role-${AWS::StackName}' AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: ec2.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore Tags: - Key: Name Value: !Sub '${ProjectName}-ec2-role' EC2InstanceProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: !Sub '${ProjectName}-ec2-profile-${AWS::StackName}' Roles: - !Ref EC2Role # EC2 Instance - No SSH key required VSCodeServerInstance: Type: AWS::EC2::Instance Properties: ImageId: !Sub '{{resolve:ssm:/aws/service/canonical/ubuntu/server/22.04/stable/current/amd64/hvm/ebs-gp2/ami-id}}' InstanceType: !Ref InstanceType SubnetId: !Ref PublicSubnet SecurityGroupIds: - !Ref VSCodeSecurityGroup IamInstanceProfile: !Ref EC2InstanceProfile BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: VolumeType: gp3 VolumeSize: !Ref VolumeSize DeleteOnTermination: true Encrypted: true UserData: Fn::Base64: !Sub | #!/bin/bash set -e # Set environment variables export HOME=/root export USER=root # Logging function log() { local message="[$(date '+%Y-%m-%d %H:%M:%S')] $1" echo "$message" | tee -a /var/log/vscode-setup.log echo "$message" > /dev/console 2>/dev/null || true } # Output password to console output_password() { local password="$1" echo "" > /dev/console 2>/dev/null || true echo "=========================================" > /dev/console 2>/dev/null || true echo "VS CODE SERVER PASSWORD" > /dev/console 2>/dev/null || true echo "=========================================" > /dev/console 2>/dev/null || true echo "Password: $password" > /dev/console 2>/dev/null || true echo "=========================================" > /dev/console 2>/dev/null || true echo "" > /dev/console 2>/dev/null || true } log "=== Starting VS Code Server setup (No SSH) ===" echo "VS Code Server setup starting..." > /dev/console 2>/dev/null || true # Wait for system initialization sleep 30 # Function to wait for package locks wait_for_apt() { local max_attempts=20 local attempt=1 while [ $attempt -le $max_attempts ]; do if fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; then log "Package manager busy, waiting... ($attempt/$max_attempts)" sleep 10 attempt=$((attempt + 1)) else return 0 fi done killall apt apt-get dpkg 2>/dev/null || true rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock dpkg --configure -a } wait_for_apt # Update system log "Updating system..." apt update && apt upgrade -y # Install dependencies log "Installing dependencies..." apt install -y curl wget nginx nodejs npm unzip # Install code-server log "Installing code-server..." export HOME=/root curl -fsSL https://code-server.dev/install.sh | sh # Generate password log "Generating password..." PASSWORD=$(openssl rand -base64 32) echo "VS Code Server Password: $PASSWORD" > /home/ubuntu/vscode-password.txt chown ubuntu:ubuntu /home/ubuntu/vscode-password.txt chmod 600 /home/ubuntu/vscode-password.txt # Output password to console output_password "$PASSWORD" # Configure code-server log "Configuring code-server..." mkdir -p /home/ubuntu/.config/code-server cat > /home/ubuntu/.config/code-server/config.yaml << EOF bind-addr: 127.0.0.1:8080 auth: password password: $PASSWORD cert: false disable-telemetry: true disable-update-check: false EOF chown -R ubuntu:ubuntu /home/ubuntu/.config # Start code-server log "Starting code-server..." systemctl enable --now code-server@ubuntu sleep 10 # Verify code-server is running for i in {1..6}; do if systemctl is-active --quiet code-server@ubuntu; then log "code-server service is running" break else log "Waiting for code-server to start... (attempt $i/6)" sleep 10 fi done # Configure Nginx log "Configuring Nginx..." cat > /etc/nginx/sites-available/vscode-server << 'EOF' server { listen 80; server_name _; # Security headers add_header X-Frame-Options DENY always; add_header X-Content-Type-Options nosniff always; add_header X-XSS-Protection "1; mode=block" always; server_tokens off; location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; proxy_cache_bypass $http_upgrade; } # Health check location /health { access_log off; return 200 "healthy\n"; add_header Content-Type text/plain; } } EOF ln -sf /etc/nginx/sites-available/vscode-server /etc/nginx/sites-enabled/ rm -f /etc/nginx/sites-enabled/default nginx -t && systemctl reload nginx # Create workspace log "Creating workspace..." mkdir -p /home/ubuntu/workspace cat > /home/ubuntu/workspace/README.md << 'WSEOF' # VS Code Server with Amazon Q CLI Welcome to your cloud development environment! ## Quick Start 1. Open terminal: `q login --use-device-flow` 2. Start coding with AI assistance: `q chat` 3. Install extensions as needed ## Available Tools - VS Code Server (web IDE) - Amazon Q CLI (AI assistant) - AWS Toolkit - Python, Node.js/npm WSEOF chown -R ubuntu:ubuntu /home/ubuntu/workspace # Install Amazon Q log "Installing Amazon Q..." sudo -u ubuntu bash -c ' cd /home/ubuntu curl --proto "=https" --tlsv1.2 -sSf "https://desktop-release.q.us-east-1.amazonaws.com/latest/q-x86_64-linux.zip" -o "q.zip" && \ unzip q.zip && \ mkdir -p ~/.local/bin && \ cp q/bin/* ~/.local/bin/ && \ chmod +x ~/.local/bin/* && \ echo "export PATH=\"\$HOME/.local/bin:\$PATH\"" >> ~/.bashrc && \ rm -rf q.zip q/ && \ ~/.local/bin/q --version ' # Verify Q installation if sudo -u ubuntu /home/ubuntu/.local/bin/q --version >/dev/null 2>&1; then log "Amazon Q installed successfully" echo "Amazon Q installed successfully" > /dev/console 2>/dev/null || true else log "Amazon Q installation failed" echo "Amazon Q installation failed" > /dev/console 2>/dev/null || true fi # Install VS Code extensions log "Installing essential extensions..." sudo -u ubuntu code-server --install-extension ms-python.python || true sudo -u ubuntu code-server --install-extension esbenp.prettier-vscode || true sudo -u ubuntu code-server --install-extension amazonwebservices.aws-toolkit-vscode || true # Get instance info with retry logic log "Getting instance information..." INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id) # Get public IP with retry and error handling PUBLIC_IP="" for i in {1..5}; do PUBLIC_IP=$(curl -s --connect-timeout 5 http://169.254.169.254/latest/meta-data/public-ipv4 2>/dev/null) if [ ! -z "$PUBLIC_IP" ] && [ "$PUBLIC_IP" != "null" ]; then log "Public IP retrieved: $PUBLIC_IP" break else log "Attempt $i: Failed to get public IP, retrying in 10 seconds..." sleep 10 fi done # Fallback if public IP is still empty if [ -z "$PUBLIC_IP" ] || [ "$PUBLIC_IP" = "null" ]; then log "Warning: Could not retrieve public IP from metadata service" PUBLIC_IP="PENDING" echo "Warning: Public IP not available yet" > /dev/console 2>/dev/null || true fi # Create info file cat > /home/ubuntu/server-info.txt << INFOEOF VS Code Server Information ========================= Instance ID: $INSTANCE_ID Public IP: $PUBLIC_IP Access URL: $([ "$PUBLIC_IP" = "PENDING" ] && echo "Check CloudFormation outputs" || echo "http://$PUBLIC_IP") Password: $PASSWORD Setup Date: $(date) Instance Type: ${InstanceType} Volume Size: ${VolumeSize}GB Quick Start: 1. q login --use-device-flow 2. q chat Note: SSH access is disabled by design for security. INFOEOF chown ubuntu:ubuntu /home/ubuntu/server-info.txt # Final console output echo "" > /dev/console 2>/dev/null || true echo "VS CODE SERVER SETUP COMPLETE!" > /dev/console 2>/dev/null || true echo "=========================================" > /dev/console 2>/dev/null || true if [ "$PUBLIC_IP" = "PENDING" ]; then echo "Access URL: Check CloudFormation outputs for IP" > /dev/console 2>/dev/null || true echo "Public IP: Will be available shortly" > /dev/console 2>/dev/null || true else echo "Access URL: http://$PUBLIC_IP" > /dev/console 2>/dev/null || true fi echo "Password: $PASSWORD" > /dev/console 2>/dev/null || true echo "Ready for iPad access!" > /dev/console 2>/dev/null || true echo "SSH disabled (web access only)" > /dev/console 2>/dev/null || true echo "=========================================" > /dev/console 2>/dev/null || true log "Setup completed successfully! No SSH access needed." if [ "$PUBLIC_IP" = "PENDING" ]; then log "Access URL: Check CloudFormation outputs for public IP" log "Public IP will be available shortly" else log "Access URL: http://$PUBLIC_IP" fi log "Password: $PASSWORD" # Test connectivity if curl -s http://localhost/health > /dev/null; then log "VS Code Server is responding correctly" echo "Setup successful! VS Code Server is ready." > /dev/console 2>/dev/null || true else log "Setup completed but health check failed" echo "Setup completed but health check failed" > /dev/console 2>/dev/null || true fi Tags: - Key: Name Value: !Sub '${ProjectName}-instance' - Key: Project Value: !Ref ProjectName Outputs: InstanceId: Description: 'ID of the EC2 instance' Value: !Ref VSCodeServerInstance InstanceType: Description: 'EC2 instance type used' Value: !Ref InstanceType VolumeSize: Description: 'EBS volume size in GB' Value: !Ref VolumeSize InstancePublicIp: Description: 'Public IP address of the EC2 instance' Value: !GetAtt VSCodeServerInstance.PublicIp VSCodeServerUrl: Description: 'URL to access VS Code Server' Value: !Join ['', ['http://', !GetAtt VSCodeServerInstance.PublicIp]] GetPasswordInstructions: Description: 'How to get the VS Code Server password' Value: !Sub | GET PASSWORD (Multiple methods): Method 1 - EC2 Console: 1. Go to EC2 Console → Instances → ${VSCodeServerInstance} 2. Actions → Monitor and troubleshoot → Get system log 3. Search for "VS CODE SERVER PASSWORD" Method 2 - Systems Manager (if needed): 1. Go to Systems Manager → Session Manager 2. Start session with ${VSCodeServerInstance} 3. Run: cat /home/ubuntu/server-info.txt NextSteps: Description: 'Next steps after deployment' Value: !Join - '' - - 'READY TO USE:\n' - '1. Wait 8-10 minutes for setup to complete\n' - '2. Get password from EC2 console output\n' - '3. Access: http://' - !GetAtt VSCodeServerInstance.PublicIp - '\n4. Enter password and start coding from iPad!\n' - '\nNo SSH needed - everything accessible via web browser!'
IMPORTANT: Please remember to DELETE YOUR STACK AFTER TESTING to avoid ongoing costs!
aws cloudformation delete-stack --stack-name remote-dev-vscode-server --region us-east-1
- Language
- English
Relevant content
- asked 9 months ago
- Accepted Answerasked 2 months ago
AWS OFFICIALUpdated 10 months ago