Deployment guide

code-server 4.145.2

Overview

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.

This guide covers the code-server 4.145.2 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud developer and API tools listing on AWS Marketplace. You choose it as the software version when you subscribe and launch.

What you get: code-server running 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/sac/data/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.145.2 bundles VS Code (Code - OSS) 1.145.2 and Node.js 24.22.1. The bundled undici HTTP library is raised to 7.30.2, which fixes every published high-severity advisory for the copy code-server ships.
  • Rate-limited sign-in. Sign-in attempts are limited per client address, on top of code-server's own limiter. nginx always replaces the X-Forwarded-For header with the address it accepted the connection from, so a client cannot spoof its address. The session cookie is always HttpOnly and Secure.
  • A sandboxed editor. code-server runs as its own account, coder, with no login shell, no sudo and no capabilities. It can write only to 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.
  • A patched base. Ubuntu 24.04 LTS is fully patched at build time, with unattended security upgrades switched on.

Image contents: the official code-server 4.145.2 release package (code-server_4.145.2_amd64.deb, checked at build time against the SHA-256 digest GitHub publishes for it), 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 bundled with VS Code's built-in Copilot extension are in /usr/share/doc/sac-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>) and avoid exposing them to the whole internet.
  • A modern browser (Chrome, Edge, Firefox or Safari).
1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick code-server 4.145.2 on Ubuntu 24.04 as the software version, choose your Region, then choose Launch through EC2.

Select m5.large, your key pair, your subnet and the security group above. VS Code and its language servers use a lot of memory, but a t3.medium is enough for light editing.

Your workspace, settings and extensions all live on the root volume, in /var/lib/code-server. The image's root volume is 16 GiB. Increase it at launch (for example to 50 GiB) if you will clone large repositories or install many tool chains. There is no separate data volume to attach.

To launch with the AWS CLI instead, 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}]'

Attach an Elastic IP to keep the instance's address, and therefore its name and certificate, the same across stops and starts.

2

Connect to your instance

Connect over SSH as the ubuntu user, the default user for code-server 4.145.2 on Ubuntu 24.04:

ssh -i <your-key.pem> ubuntu@<public-ip>
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. Confirm that everything is running:

systemctl is-active code-server-firstboot code-server-egress-guard code-server nginx \
  && sudo test -f /var/lib/sac/data/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, and 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
4

Get your address and password

Run sudo cat /var/lib/sac/data/code-server-credentials.txt to see your editor's address and password. The file is root-only and holds these settings, all generated for this instance:

sudo sed -n 's/^\(CODE_SERVER_[A-Z0-9_]*\)=.*/\1/p' /var/lib/sac/data/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. Settings that are not secret can be shown on their own:

sudo grep -E '^CODE_SERVER_(URL|TLS_SHA256_FINGERPRINT)=' /var/lib/sac/data/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

code-server's configuration stores the password only as an argon2id hash, and only root and the coder account can read it:

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
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, verify that it is your instance's certificate. The SHA-256 fingerprint in the browser's certificate viewer must match CODE_SERVER_TLS_SHA256_FINGERPRINT. Alternatively, 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 remove the warning permanently, add code-server.crt to your machine's trusted certificates (Keychain Access on macOS, certmgr on Windows). You can also use your own domain and a public certificate (Step 10).

On the code-server 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 select 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 select Clone Git Repository on the welcome page to clone a repository.

6

Use the terminal

Open a terminal with Terminal > New Terminal (or Ctrl+). It is a bash shell on the instance, running as thecoderaccount in your workspace. Everything the editor starts (the terminal, tasks, debuggers, language servers and extensions) runs ascoder, 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.

7

Install extensions

Open the Extensions view (the blocks icon on the left). Extensions come from Open VSX, the open extension registry that code-server uses. It carries most popular extensions, including Python, Go, Rust, Java, Prettier, ESLint and GitLens.

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 you need one of them, download its .vsix from the publisher and install it with Extensions > ... > Install from VSIX.

8

Run the self-test

The image ships a self-test. It verifies that:

  • the services and listening ports are as expected
  • a request without a session gets the sign-in page and no editor connection
  • a wrong password is refused
  • the password in the credentials file signs in over HTTPS with this instance's certificate, with an HttpOnly and Secure session cookie
  • the session opens the editor with Open VSX as its gallery
  • the editor's WebSocket connection is accepted from the editor's own page and refused from any other site

Run it:

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. If you have just mistyped the password, wait a minute between runs.

9

Preview web apps through the port proxy

Start a web app in the editor's terminal, for example python3 -m http.server 3000. Then open it in your browser at one of two routes:

  • https://<your instance's name>/proxy/3000/: the app sees paths without the /proxy/3000 prefix.
  • https://<your instance's name>/absproxy/3000/: for apps that expect their full path.

The proxy reaches only this instance, and both routes require 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.

A page served through the proxy comes from the same site as the editor. Run only apps you trust there, and do not open untrusted pages through it. The browser treats such a page as part of the editor's site, so it could act on your signed-in session. The session cookie itself stays HttpOnly, so the page cannot read it.

10

Change the password or use your own domain

To give the editor a new random password, run the command below. The old password and every session signed in with it stop working at once. Then read the new CODE_SERVER_PASSWORD with sudo cat /var/lib/sac/data/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 the address stable), then set it as 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.

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/sac/data/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 /login are 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 answers 429. The limit applies to every spelling of the path (//login, /LOGIN, /%6Cogin, /login;x and so on), because nginx matches it after normalising the path.
  • X-Forwarded-For. nginx replaces any X-Forwarded-For header with the address it accepted the connection from and blanks X-Real-IP, Forwarded and 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, HEAD or OPTIONS) and every editor WebSocket connection must come from the editor's own page: with another site's Origin header nginx answers 403, 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; TRACE and other unusual methods get 405.
  • Headers. Responses carry Strict-Transport-Security, X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN, a Content-Security-Policy of frame-ancestors 'self', Referrer-Policy: same-origin and a Permissions-Policy that 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.

12

Back up and restore

Everything the editor keeps, including your workspace, settings, extensions and terminal history, is in /var/lib/code-server. Take an EBS snapshot of the root volume, or copy the directory 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 code-server 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.

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.145.2 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.145.2' sed -n '1,3p' /usr/share/doc/sac-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.145.2 code-server 4.145.2 (github.com/coder/code-server release v4.145.2, 2026-11-07), MIT (code-server-LICENSE) code-server_4.145.2_amd64.deb sha256 7eddbdeaab34380bde3ac3bbadce58fe2f5722c4877f331ea593ae797c2517e1 (= GitHub's published asset digest) bundles Code - OSS 1.145.2 (commit 302bc77436721f5a474e06d1fe34cf8f86195d5e) 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. A refused configuration stops code-server once and for all instead of retrying it in a loop: fix the file, then run sudo systemctl restart code-server. A crash, by contrast, restarts it automatically. 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/sac-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.

14

Keep it up to date

Ubuntu security updates, including nginx, install automatically. To apply all pending updates now, run:

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: sales@secureanycloud.com
  • Website: https://secureanycloud.com

Other versions on this listing are covered in the developer and API tools guide.

Deploy on AWS

Launch code-server 4.145.2 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS