code-server 4.144.0 on AWS User Guide
Overview
This guide covers launching and using the code-server 4.144.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg developer and API tools listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.
code-server runs VS Code on a server and serves it to your browser. You get the full editor, an integrated terminal, source control and extensions, from any machine with a browser, while the code, the builds and the tools stay on your EC2 instance.
The cloudimg image runs code-server as a private, single-user service:
- HTTPS from the first boot. nginx serves code-server on port 443 with a certificate generated on your instance's first boot. Port 80 only redirects to HTTPS, and a request for any name other than your instance's own gets no answer.
- No default password. The image contains no password, no certificate and no host name. First boot generates them and writes them to
/var/lib/cloudimg/code-server-credentials.txt(root only). code-server's configuration keeps only an argon2id hash of the password, never the password itself. - The current editor. code-server 4.144.0 bundles VS Code (Code - OSS) 1.144.0 and Node.js 24.22.1. The bundled
undiciHTTP library is raised to 7.30.1, which fixes four high-severity advisories in the copy code-server ships. - Sign-in that cannot be dodged. Sign-in attempts are limited per client address, on top of code-server's own limiter. nginx always replaces the
X-Forwarded-Forheader with the address it accepted the connection from, so a client cannot pretend to be someone else, and the session cookie is alwaysHttpOnlyandSecure. - A sandboxed editor. code-server runs as its own account,
coder, with no login shell, nosudoand no capabilities, and it can write only its own home directory. Nothing the editor runs, including its terminal and extensions, can reach the EC2 instance metadata service. - Extensions from Open VSX, the open registry code-server uses. Telemetry and update checks are off.
- Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.
What is on the image: the official code-server 4.144.0 release package (code-server_4.144.0_amd64.deb, checked against the SHA-256 digest GitHub publishes for it at build time), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. The build record, code-server's MIT licence and the licence of the GitHub Copilot CLI that VS Code's built-in Copilot extension bundles are in /usr/share/doc/cloudimg-code-server/.
Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large
Prerequisites
- An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet.
- A security group that allows TCP 443 (the editor), TCP 80 (redirects to HTTPS) and TCP 22 (SSH). The editor gives whoever signs in a terminal on the instance, so open 443 and 22 only to your own networks (for example
<your-mgmt-cidr>), never to the whole internet if you can avoid it. - A modern browser (Chrome, Edge, Firefox or Safari).
Step 1: Launch the instance
In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick code-server 4.144.0 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large (VS Code and its language servers like memory; a t3.medium is enough for light editing), your key pair, your subnet and the security group above.
Your workspace, settings and extensions all live on the root volume, in /var/lib/code-server. The image's root volume is 16 GiB; give it more at launch if you will clone large repositories or install many tool chains (for example 50 GiB). There is no separate data volume to attach.
With the AWS CLI (replace the values in angle brackets):
aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
--instance-type m5.large --key-name <your-key-pair> \
--security-group-ids <your-security-group> --subnet-id <your-subnet> \
--metadata-options HttpTokens=required,HttpEndpoint=enabled \
--block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":50,"VolumeType":"gp3"}}]' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=code-server}]'
An Elastic IP keeps the instance's address, and so its name and certificate, the same across stops and starts.
Step 2: Connect to your instance
| Version | SSH user |
|---|---|
| code-server 4.144.0 on Ubuntu 24.04 | ubuntu |
ssh -i <your-key.pem> ubuntu@<public-ip>
Step 3: Check the services
First boot takes under a minute: it sets the server's name, generates the certificate and the password, and only then starts code-server and nginx. Check that everything is running:
systemctl is-active code-server-firstboot code-server-egress-guard code-server nginx \
&& sudo test -f /var/lib/cloudimg/code-server-firstboot.done && echo FIRSTBOOT_DONE
active
active
active
active
FIRSTBOOT_DONE
Only SSH and nginx listen on the network. code-server listens on the loopback address 127.0.0.1:8080 behind nginx (its processes show up as MainThread):
sudo ss -lntp | awk 'NR>1 && $4 !~ /^127\.0\.0\.5[34]/ {print $4, $6}' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort -u | tee /dev/stderr \
| awk '$1 !~ /^127\./ {sub(/.*:/, "", $1); print $1}' | sort -un | xargs | grep -qx '22 80 443' && echo "network listeners: 22 80 443"
0.0.0.0:22 users:(("sshd"),("systemd"))
0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
127.0.0.1:8080 users:(("MainThread"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))
network listeners: 22 80 443
Step 4: Get your address and password
Run sudo cat /var/lib/cloudimg/code-server-credentials.txt to see your editor's address and password. The file is readable by root only and holds these settings, all generated for this instance:
sudo sed -n 's/^\(CODE_SERVER_[A-Z0-9_]*\)=.*/\1/p' /var/lib/cloudimg/code-server-credentials.txt | tee /dev/stderr \
| grep -qx CODE_SERVER_PASSWORD && echo "credentials present"
CODE_SERVER_URL
CODE_SERVER_HOST
CODE_SERVER_PASSWORD
CODE_SERVER_TLS_CERT
CODE_SERVER_TLS_SHA256_FINGERPRINT
credentials present
CODE_SERVER_URL is https://<your instance's public DNS name>/ and CODE_SERVER_PASSWORD is the password. The settings that are not secret can be shown on their own:
sudo grep -E '^CODE_SERVER_(URL|TLS_SHA256_FINGERPRINT)=' /var/lib/cloudimg/code-server-credentials.txt \
| tee /dev/stderr | grep -q '^CODE_SERVER_URL=https://'
CODE_SERVER_URL=https://ec2-203-0-113-10.compute-1.amazonaws.com/
CODE_SERVER_TLS_SHA256_FINGERPRINT=49:47:FC:...:32
The password is stored in code-server's configuration only as an argon2id hash, and the configuration is readable only by root and the coder account:
sudo grep -c -F "hashed-password: '\$argon2id\$v=19\$" /etc/code-server/config.yaml | grep -qx 1 \
&& ! sudo grep -qE '^[[:space:]]*password:' /etc/code-server/config.yaml \
&& sudo stat -c '%U:%G %a' /etc/code-server/config.yaml | tee /dev/stderr | grep -qx 'root:coder 640' && echo "hash only"
root:coder 640
hash only
Step 5: Sign in from your browser
Open CODE_SERVER_URL in your browser. The certificate is self-signed and was generated on your instance, so the browser warns about it the first time. Before you accept it, check that it is your instance's certificate: the browser's certificate viewer shows a SHA-256 fingerprint that must match CODE_SERVER_TLS_SHA256_FINGERPRINT. Or copy the certificate to your machine and compare it there:
scp -i <your-key.pem> ubuntu@<public-ip>:/etc/ssl/code-server/code-server.crt ./code-server.crt
openssl x509 -in code-server.crt -noout -fingerprint -sha256
To stop the warning altogether, add code-server.crt to your machine's trusted certificates (Keychain Access on macOS, certmgr on Windows), or use your own domain and a public certificate (Step 10).
code-server shows its sign-in page. Enter CODE_SERVER_PASSWORD:

The editor opens on the empty workspace folder, /var/lib/code-server/workspace:

Your session lasts until you sign out (Sign out in the menu at the top left) or the password changes. Use File > Open Folder to open another folder under /var/lib/code-server, or clone a repository with Clone Git Repository on the welcome page.
Step 6: Use the terminal
Open a terminal with Terminal > New Terminal (or Ctrl+`). It is a bash shell on the instance, running as the coder account in your workspace. Everything the editor starts (the terminal, tasks, debuggers, language servers and extensions) runs as coder, inside the code-server service, and can reach the internet (to install packages, fetch dependencies or push to git) but not the EC2 instance metadata service. The screenshot below is that check, run in the editor's own terminal: the metadata service gets no answer (000) and a public site answers (200):

You can run the same check over SSH. The metadata service is refused for the coder account and still answers for ubuntu (your SSH account), and the extension registry answers for coder:
{ sudo -u coder curl -s -m 5 -o /dev/null -w 'coder -> metadata: %{http_code}\n' -X PUT http://169.254.169.254/latest/api/token -H 'X-aws-ec2-metadata-token-ttl-seconds: 60'
sudo -u coder curl -s -m 20 -o /dev/null -w 'coder -> open-vsx.org: %{http_code}\n' 'https://open-vsx.org/api/-/search?size=1'
curl -s -m 5 -o /dev/null -w 'ubuntu -> metadata: %{http_code}\n' -X PUT http://169.254.169.254/latest/api/token -H 'X-aws-ec2-metadata-token-ttl-seconds: 60'
} | tee /dev/stderr | xargs | grep -qx 'coder -> metadata: 000 coder -> open-vsx.org: 200 ubuntu -> metadata: 200' && echo "metadata blocked for the editor"
coder -> metadata: 000
coder -> open-vsx.org: 200
ubuntu -> metadata: 200
metadata blocked for the editor
Two things enforce this: the code-server service's own network filter (systemd IPAddressDeny) and an nftables rule that rejects the metadata addresses for the coder account anywhere on the instance. So a command, extension or dependency you run in the editor cannot read the instance's IAM role credentials. If your code needs AWS credentials, configure them for the coder account yourself (for example with aws configure in the editor's terminal); give them only the permissions that code needs.
The editor's terminal can reach your VPC and other private addresses, like any shell on the instance: code-server itself makes no outbound requests on anyone else's behalf (its port proxy in Step 9 reaches only this instance), so the image does not fence private networks.
The coder account has no sudo. To install system packages, use your SSH session as ubuntu (sudo apt-get install ...); tools that install per user (pip install --user, npm with a prefix, rustup, nvm) work in the editor's terminal.
Step 7: Install extensions
Open the Extensions view (the blocks icon on the left). Extensions come from Open VSX, the open extension registry code-server uses, which carries most popular extensions (Python, Go, Rust, Java, Prettier, ESLint, GitLens and many more):

Extensions install into /var/lib/code-server/.local/share/code-server/extensions. Some Microsoft extensions are published only on the Microsoft Marketplace, whose terms allow them only in Microsoft's own VS Code products; if one you need is missing, download its .vsix from the publisher and install it with Extensions > ... > Install from VSIX.
Step 8: Run the self-test
The image ships a self-test that checks the services and listening ports, that a request without a session gets the sign-in page and no editor connection, that a wrong password is refused, that the password in the credentials file signs in over HTTPS with this instance's certificate (with an HttpOnly and Secure session cookie), that the session opens the editor with Open VSX as its gallery, and that the editor's WebSocket connection is accepted from the editor's own page and refused from any other site:
sudo code-server-selftest | tail -n 1 | tee /dev/stderr | grep -q '^CODE_SERVER_SELFTEST_OK '
CODE_SERVER_SELFTEST_OK wrong password refused, the note's password logs in over https://ec2-203-0-113-10.compute-1.amazonaws.com/ (HttpOnly+Secure session), workbench + same-origin WebSocket 101, cross-origin WebSocket 403, no session = login page
code-server's own sign-in limiter allows two failed sign-ins a minute, so wait a minute between runs if you have just mistyped the password.
Step 9: Preview web apps through the port proxy
When you run a web app in the editor's terminal (for example python3 -m http.server 3000), open it in your browser at https://<your instance's name>/proxy/3000/ (the app sees paths without the /proxy/3000 prefix) or https://<your instance's name>/absproxy/3000/ (for apps that expect their full path). The proxy reaches only this instance, and both routes need you to be signed in: without a session they answer 401 (or redirect to the sign-in page). The session cookie is never passed on to the app over plain HTTP.
One thing to keep in mind: a page served through the proxy comes from the same site as the editor. Only run apps you trust there, and do not open untrusted pages through it, because a page that the browser treats as part of the editor's site could act on your signed-in session (the session cookie itself stays HttpOnly, so the page cannot read it).
Step 10: Change the password or use your own domain
To give the editor a new random password (the old one, and every session signed in with it, stop working at once), run the command below, then read the new CODE_SERVER_PASSWORD with sudo cat /var/lib/cloudimg/code-server-credentials.txt:
sudo code-server-rotate-password
The new password is hashed with argon2id, only the hash goes into /etc/code-server/config.yaml, and the password never appears on a command line or in a log.
To use your own domain, point a DNS name at the instance (an Elastic IP keeps it stable), then make it the server's name. This generates a new certificate for that name and pins nginx to it:
sudo code-server-set-site code.example.com
To use a certificate from a public CA instead, for example one issued by certbot with a DNS challenge, run code-server-set-site first, then replace /etc/ssl/code-server/code-server.crt (full chain) and /etc/ssl/code-server/code-server.key (mode 0600) and run sudo systemctl reload nginx. Browsers then show no warning.
Step 11: What the front door refuses
nginx answers only for the server's own names and passes requests to code-server on loopback:
H="$(sudo sed -n 's/^CODE_SERVER_HOST=//p' /var/lib/cloudimg/code-server-credentials.txt)"
{ curl -s -o /dev/null -w '%{http_code}\n' --cacert /etc/ssl/code-server/code-server.crt --resolve "$H:443:127.0.0.1" "https://$H/healthz"
curl -s -o /dev/null -w '%{http_code}\n' -k -H 'Host: someone-else.example' https://127.0.0.1/
curl -s -o /dev/null -w '%{http_code}\n' --resolve "$H:80:127.0.0.1" "http://$H/"
curl -s -o /dev/null -w '%{http_code}\n' --cacert /etc/ssl/code-server/code-server.crt --resolve "$H:443:127.0.0.1" -X POST -H 'Origin: https://evil.example' "https://$H/healthz"
} | xargs | tee /dev/stderr | grep -qx '200 000 301 403' && echo "front door OK"
200 000 301 403
front door OK
- Sign-in limits. Requests to
/loginare limited to 6 a minute per client address (bursts of 10), on top of code-server's own limiter; the rest of the editor to 50 requests a second (bursts of 1000), with at most 128 connections per address. Over the limit, nginx answers429. The limit applies to every spelling of the path (//login,/LOGIN,/%6Cogin,/login;xand so on), because nginx matches it after normalising the path. - X-Forwarded-For. nginx replaces any
X-Forwarded-Forheader with the address it accepted the connection from and blanksX-Real-IP,Forwardedand similar headers, so a client cannot change the address the limits and code-server's failed-sign-in log use. If you put a load balancer in front of the instance, every client then shares the load balancer's address for the limits. - Cross-site requests. A request that changes something (anything but
GET,HEADorOPTIONS) and every editor WebSocket connection must come from the editor's own page: with another site'sOriginheader nginx answers403, and code-server refuses a cross-site WebSocket as well. - Sizes and methods. Uploads are limited to 256 MiB and the sign-in form to 4 KiB;
TRACEand other unusual methods get405. - Headers. Responses carry
Strict-Transport-Security,X-Content-Type-Options: nosniff,X-Frame-Options: SAMEORIGIN, aContent-Security-Policyofframe-ancestors 'self',Referrer-Policy: same-originand aPermissions-Policythat turns off the camera, microphone, location, payment and USB APIs, and nginx does not advertise its version.
What is logged: nginx's access log (/var/log/nginx/access.log) records the client address, request path and status, without query strings. nginx's error log records only start-up problems, not refused requests, so a password in a rejected request never reaches the disk. code-server logs failed sign-ins (with the client address nginx saw, never the password) to the journal: journalctl -u code-server.
Step 12: Back up and restore
Everything the editor keeps (your workspace, settings, extensions and terminal history) is in /var/lib/code-server. Take an EBS snapshot of the root volume, or copy it off the instance:
sudo tar -C /var/lib/code-server -czf code-server-$(date +%F).tar.gz .
To restore into a new instance of this image, stop code-server, unpack the archive into /var/lib/code-server as coder, and start it again: sudo systemctl stop code-server && sudo -u coder tar -C /var/lib/code-server -xzf - < code-server-<date>.tar.gz && sudo systemctl start code-server. Keep your code in git as well: a repository pushed to a remote is the best backup.
Step 13: Security notes and the build record
The certificate generated at first boot is a server certificate only: it cannot sign other certificates.
openssl x509 -in /etc/ssl/code-server/code-server.crt -noout -ext basicConstraints,keyUsage,extendedKeyUsage | tee /dev/stderr | grep -q 'CA:FALSE'
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication
The package is the 4.144.0 release, checked against GitHub's published digest at build time, and the editor's account has no login shell and no sudo:
dpkg-query -W -f='${Package} ${Version}\n' code-server | tee /dev/stderr | grep -qx 'code-server 4.144.0'
sed -n '1,3p' /usr/share/doc/cloudimg-code-server/BUILD-INFO.txt
getent passwd coder | cut -d: -f1,6,7 | tee /dev/stderr | grep -qx 'coder:/var/lib/code-server:/usr/sbin/nologin' \
&& sudo -l -U coder 2>&1 | grep -q 'not allowed to run sudo' && echo "coder: no login shell, no sudo"
code-server 4.144.0
code-server 4.144.0 (github.com/coder/code-server release v4.144.0, 2026-10-29), MIT (code-server-LICENSE)
code-server_4.144.0_amd64.deb sha256 45f5533b502edac68b7eb09c5cec44fd281f80264af9a87fde0bfb2c5e190615 (= GitHub's published asset digest)
bundles Code - OSS 1.144.0 (commit ee31cdbe02c07b3ba718007f221d14c890696c61) and Node.js v24.22.1
coder:/var/lib/code-server:/usr/sbin/nologin
coder: no login shell, no sudo
code-server runs as coder with no capabilities and NoNewPrivileges, sees the rest of the system read-only (ProtectSystem=strict, writable only under /var/lib/code-server), has private /tmp and /dev, and cannot see other users' processes. It cannot read the credentials file or the TLS key. Before every start, code-server-config-check refuses a configuration that turns authentication off, stores a plaintext password, listens anywhere but loopback, or sets proxy-domain, trusted-origins, skip-auth-preflight, cert or link, and refuses PASSWORD, HASHED_PASSWORD or a Microsoft extension gallery in the service's environment. Some sandbox settings are deliberately relaxed because an IDE needs them: JIT compilation (no MemoryDenyWriteExecute), any system call (no SystemCallFilter, so debuggers and compilers work), a full /proc for its own processes, and netlink sockets.
Do not run the code-server command yourself as ubuntu or root: with no --config it writes a new configuration with a generated password into that account's home. Manage the service with systemctl and the code-server-* helpers instead.
code-server is MIT licensed; its licence is in /usr/share/doc/cloudimg-code-server/code-server-LICENSE. VS Code's built-in Copilot extension (off until you sign in to GitHub) bundles the GitHub Copilot CLI, redistributed unmodified under its own licence, which is in the same directory. nginx comes from Ubuntu's main archive.
Step 14: Keep it up to date
Ubuntu security updates, including nginx, install automatically. To apply everything now:
sudo apt-get update && sudo apt-get upgrade -y
New code-server releases arrive as new versions of the AWS Marketplace listing: launch the new version, restore /var/lib/code-server into it (Step 12) and run sudo code-server-set-site with your domain if you use one. code-server is installed from its release package, not from an apt repository, so apt-get upgrade does not change it.
Support
- Email: support@cloudimg.co.uk
- Website: https://www.cloudimg.co.uk
The other versions on this listing are on the developer and API tools guide.