こんにちは、近藤(りょう)です!
以前、プライベートサブネットに配置した AWS Cloud9 をセットアップする 記事を書きましたが、Cloud9 の新規利用が制限されたことで、今後 Cloud9 を利用する選択肢を見直す必要があるかもしれません。
blog.serverworks.co.jp
AWS Cloud9 を利用していたハンズオン手順や、日常的に使用していた方々も、代替手法を検討されているのではないでしょうか。例えば、以下のリンクでは AWS IDE Toolkits や AWS CloudShell への移行方法が紹介されています。
aws.amazon.com
AWS IDE Toolkits や AWS CloudShell を利用する方法もありますが、ローカル環境に依存せず、各チームが共通の 統合開発環境 (IDE) を簡単に初期セットアップ&カスタマイズできる仕組みを導入したい場合もあるのではないでしょうか。
そこで代替案の一つとして、今回は code-serverをDocker上にセットアップし、リモート統合開発環境 (IDE)として利用してみることを試してみます。
code-server について
Coder社が提供するOSSのリモート統合開発環境 (IDE) です。ブラウザ上でコードの記述、実行、デバッグが可能です。
VSCodeをベースにしたWebベースのIDEであるため、見た目や操作感はVisual Studio Code (VS Code) とほぼ同じです。そのため、普段からVS Codeを利用している方は、違和感なく使用できます。
code-server のドキュメント
code-serverの詳細はドキュメントを参照ください。 coder.com
Docker用のイメージを利用する場合の制約
Our official image supports amd64 and arm64. For arm32 support, you can use a community-maintained code-server alternative.
code-serverの公式イメージは amd64(一般的な64ビットIntel/AMDアーキテクチャ)およびarm64(64ビットARMアーキテクチャ)に対応しています。 coder.com
今回の構成
Public Subnet 上に EC2(Amazon Linux 2023)をデプロイし、Docker を使用して code-server をインストールします。この構成では、セキュリティグループで利用ユーザーの IP アドレスのみを許可します。また、code-server への接続は、SSL 通信を使用して 443 番ポート経由で行います。

環境構築してみる
東京リージョンに code-server を構築してみます。
CFn 実行
東京リージョンでCFnをぽちっとな。
本code-serverは systemdによる自動起動制御の設定をしているため、サーバーを停止してから再起動しても正常に動作します。
パラメータ は必要に応じて適宜修正してください。
(もしEC2からAWSリソースに対して何か必要な権限が必要な場合はロールの修正をお願いします。)
AWSTemplateFormatVersion: "2010-09-09"
Description: "CloudFormation template to create a code-server with specified requirements."
Parameters:
AllowedIP:
Type: String
Description: "The IP address allowed to access the instance on port 443 (e.g., 203.0.113.0/32)."
ConstraintDescription: "Must be a valid CIDR notation (e.g., 203.0.113.0/32)."
AllowedPattern: "^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}/[0-9]{1,2}$"
ImageId:
Type: String
Description: "The AMI ID for the EC2 instance (e.g., ami-12345678)."
Default: "ami-023ff3d4ab11b2525"
ConstraintDescription: "Must be a valid AMI ID."
Resources:
# VPC
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "10.0.0.0/16"
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: "CfnVPC"
# Internet Gateway
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: "CfnInternetGateway"
# Attach Internet Gateway to VPC
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
# Public Subnet
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: "10.0.0.0/24"
MapPublicIpOnLaunch: true
AvailabilityZone: !Select [ 0, !GetAZs "" ]
Tags:
- Key: Name
Value: "CfnPublicSubnet"
# Route Table
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: "CfnRouteTable"
# Route for Internet Access
PublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref RouteTable
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref InternetGateway
# Associate Route Table with Subnet
RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref RouteTable
# Security Group
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Allow HTTPS access from specified IP"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: !Ref AllowedIP
Tags:
- Key: Name
Value: "CfnSecurityGroup"
# IAM Role for SSM Access
IAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Path: "/"
Tags:
- Key: Name
Value: "CfnSSMRole"
Policies:
- PolicyName: "SSMPutParameterPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ssm:PutParameter
Resource:
Fn::Sub: arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/code-server/login-password
# Instance Profile
IAMInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles:
- !Ref IAMRole
InstanceProfileName: "CfnSSMInstanceProfile"
# EC2 Instance
EC2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.micro
SubnetId: !Ref PublicSubnet
SecurityGroupIds:
- !Ref SecurityGroup
IamInstanceProfile: !Ref IAMInstanceProfile
ImageId: !Ref ImageId
Tags:
- Key: Name
Value: "code-server-docker"
UserData:
Fn::Base64: |
#!/bin/bash
# ------------------------------------------------
# 変数セット
# ------------------------------------------------
USER_NAME="ec2-user"
USER_HOME="/home/ec2-user"
CODE_SERVER_PASSWORD=$(head /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 25)
USER_ID=1000
GROUP_ID=1000
DOCKER_USER=${USER_NAME}
TEMP_DIR="/tmp"
# ------------------------------------------------
# インストール
# ------------------------------------------------
sudo dnf install -y git docker
sudo usermod -a -G docker ${USER_NAME}
DOCKER_COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | jq -r .tag_name)
sudo curl -L "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo systemctl restart docker.service
# ------------------------------------------------
# Docker定義
# ------------------------------------------------
# Dockerfileの作成
sudo cat <<'EOF' > ${TEMP_DIR}/Dockerfile
# ベースとなるイメージを指定(codercom/code-serverの最新バージョン)
FROM codercom/code-server:latest
# コンパイル時に使用する変数を定義
# USER_ID: ユーザーのID(uid)
# GROUP_ID: ユーザーが所属するグループのID(gid)
# DOCKER_USER: 使用するユーザー名
ARG USER_ID
ARG GROUP_ID
ARG DOCKER_USER
# rootユーザーで実行(初期セットアップ時に必要なパーミッションを取得するため)
USER root
# 必要なパッケージ(libcap2-bin)をインストール
# setcapを使用するために必要
RUN apt-get update && apt-get install -y libcap2-bin
# 'coder'ユーザーのIDとグループIDをARGで渡された値に変更
# これにより、ホストとコンテナ内で一致するUID/GIDを使用
RUN usermod -u $USER_ID coder && \
groupmod -g $GROUP_ID coder
# ノードのサービスバインディングに必要な権限(cap_net_bind_service)を付与
# これにより、ポート番号が1024未満のポートにサービスをバインドする権限を付与
RUN setcap 'cap_net_bind_service=+ep' /usr/lib/code-server/lib/node
# 作業ディレクトリを設定
WORKDIR /home/coder/project
# coderユーザーに切り替え
USER coder
# コンテナ起動時にdumb-initとcode-serverをエントリーポイントとして設定
ENTRYPOINT ["dumb-init", "code-server"]
EOF
sudo chown ${USER_ID}:${GROUP_ID} ${TEMP_DIR}/Dockerfile
sudo mv ${TEMP_DIR}/Dockerfile ${USER_HOME}/Dockerfile
# docker-compose.ymlの作成
cat <<'EOF' > ${TEMP_DIR}/docker-compose.yml
services:
code-server:
# code-serverサービスの設定
build:
# Dockerfileのビルド設定
context: .
args:
# Dockerfileに渡す引数(ユーザーID、グループID、ユーザー名)
USER_ID: ${USER_ID}
GROUP_ID: ${GROUP_ID}
DOCKER_USER: ${DOCKER_USER}
# ポートの公開設定
ports:
- "443:443" # ホストの443ポートをコンテナの443ポートにマッピング(HTTPSポート)
# ボリュームのマウント設定
volumes:
- "$HOME/.config:/home/coder/.config"
- "$PWD:/home/coder/project"
# 環境変数の設定
environment:
- DOCKER_USER=${DOCKER_USER}
# ユーザー設定
user: "${USER_ID}:${GROUP_ID}"
# コンテナの再起動設定
restart: always
EOF
sudo chown ${USER_ID}:${GROUP_ID} ${TEMP_DIR}/docker-compose.yml
sudo mv ${TEMP_DIR}/docker-compose.yml ${USER_HOME}/docker-compose.yml
# ------------------------------------------------
# code-server定義
# ------------------------------------------------
# code-serverのディレクトリ作成
sudo mkdir -p ${USER_HOME}/.config/code-server
sudo chown ${USER_ID}:${GROUP_ID} ${USER_HOME}/.config
# config.yamlの作成
cat <<EOF > ${TEMP_DIR}/config.yaml
bind-addr: 0.0.0.0:443
auth: password
password: ${CODE_SERVER_PASSWORD}
cert: true
EOF
sudo chown ${USER_ID}:${GROUP_ID} ${TEMP_DIR}/config.yaml
sudo mv ${TEMP_DIR}/config.yaml ${USER_HOME}/.config/code-server/config.yaml
# ------------------------------------------------
# systemd定義
# ------------------------------------------------
# /etc/systemd/system/docker-compose-app.serviceの作成
cat <<EOF > ${TEMP_DIR}/docker-compose-app.service
[Unit]
Description=Docker Compose Application
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/home/ec2-user
Environment=USER_ID=${USER_ID}
Environment=GROUP_ID=${GROUP_ID}
Environment=DOCKER_USER=${DOCKER_USER}
Environment=PWD=/home/ec2-user
Environment=HOME=${USER_HOME}
ExecStart=/usr/local/bin/docker-compose up -d
ExecStop=/usr/local/bin/docker-compose down
[Install]
WantedBy=multi-user.target
EOF
sudo chown ${USER_ID}:${GROUP_ID} ${TEMP_DIR}/docker-compose-app.service
sudo mv ${TEMP_DIR}/docker-compose-app.service /etc/systemd/system/docker-compose-app.service
sudo systemctl daemon-reload
# ------------------------------------------------
# code-server起動
# ------------------------------------------------
sudo chown -R ${USER_ID}:${GROUP_ID} ${USER_HOME}/.docker
sudo systemctl enable docker-compose-app.service
sudo systemctl start docker-compose-app.service ; sudo systemctl status docker-compose-app.service
aws ssm put-parameter --name "/code-server/login-password" --value ${CODE_SERVER_PASSWORD} --type SecureString --key-id "alias/aws/ssm" --overwrite
Outputs:
PublicIP:
Description: "code-server URL"
Value: !Join
- ""
-
- "https://"
- !GetAtt EC2Instance.PublicIp
- "/"
UserDataSSMParameter:
Description: "SSM Parameter code-server login password output"
Value: "/code-server/login-password"
CFn で指定する Parameters について
先ほどのCFnを実行する時に Parameters の設定が必要になります。

- AllowIP
- 接続を許可するIP アドレスを
/32指定で入力してください。
確認くん 等を利用し、ご自身のグローバルIPの確認してください。
- 接続を許可するIP アドレスを
- ImageId
- 任意の Amazon Linux 2023 の AMI ID を指定してください。(本記事では ami-023ff3d4ab11b2525 を使用して検証しています。)
CFn 状態確認
作成したスタックが完了していれば、code-serverのデプロイは完了です。
CFn 実行が完了すると、スタックの「出力」セクションに以下の内容が確認できます。

- PublicIP
- code-serverの接続URL
- UserDataSSMParameter
- code-serverのログインパスワードを格納している AWS Systems Manager (SSM) パラメータストアの情報
"/code-server/login-password" に保存されます。
- code-serverのログインパスワードを格納している AWS Systems Manager (SSM) パラメータストアの情報
AWS Systems Manager (SSM) パラメータストアの "/code-server/login-password" に code-server のログインパスワード情報が保存されています。
(復号化された値を表示にチェックを入れると文字列が参照できます。)

code-serverへの接続
code-server の接続 URL にアクセスします。(スタックの「出力」セクションのPublicIPがURLとなります。)
証明書を設定していないため、「この接続ではプライバシーが保護されません」と表示されますがそのまま接続を進めてください。
(「詳細設定」-「xxxxxにアクセスする(安全ではありません)」を押下する。)

SSM パラメータストアに保存されたパスワードを入力し、「SUBMIT」を押下します。

code-server にログインできたら、必要な設定を行い、利用を開始してください。

エラーメッセージについて
An SSL certificate error occurred when fetching the script.

サーバー証明書を設定していないため出力されていますので現在の設定の場合は問題ありません。 以下、参考となりますが適切なサーバー証明書を導入することで回避できます。
code-serverの削除
作成したCFnのスタックを削除してください。
パラメータストアの情報は自動で削除されないため、"/code-server/login-password" の削除もお願いします。
まとめ
個人的ですがCoder社はRe: invent 2024で「The enterprise development environment (sponsored by Coder)」(訳:エンタープライズ開発環境 (Coder 提供))でセッションをしているようなので気になっています。
参考:Join Coder at AWS re:Invent 2024
私は VS Code を利用しているため、違和感なく使用できております。 ハンズオン等のAWS Cloud9 の代替として code-server を試すのはいかがでしょうか。
近藤 諒都
(記事一覧)カスタマーサクセス部CS5課
夜行性ではありません。朝活派です。
趣味:お酒、旅行、バスケ、掃除、家庭用パン作り(ピザも)など
2025 Japan AWS All Certifications Engineers