code-server is an open source project by Coder that runs Visual Studio Code on a remote server and serves it to any web browser. Your code, terminals and extensions live on the server, so you can work from a laptop, a tablet or a borrowed machine with the same environment every time.
In this tutorial you will install code-server on Ubuntu 24.04, run it as a systemd service for your user, protect it with a hashed password, and publish it at https://code.your_domain behind Nginx with a free Let's Encrypt certificate.
Prerequisites
To follow this tutorial, you will need:
- A server running Ubuntu 24.04 LTS with at least 2 GB of RAM (1 GB works for light use, but the language servers of most extensions need more). A CubePath VPS with 2 vCPU and 4 GB of RAM is a comfortable size.
- A non-root user with
sudoprivileges. The IDE will run as this user, and its integrated terminal will have the same permissions. - Nginx installed, with UFW allowing SSH and the
Nginx Fullprofile. - A domain or subdomain, for example
code.your_domain, with a DNS A record pointing toyour_server_ip. Let's Encrypt needs this record to issue the certificate.
WarningAnyone who logs in to code-server gets a shell on your server as your user. Always put it behind HTTPS with a strong password, and never expose port 8080 directly to the Internet.
Step 1 - Installing code-server
Coder publishes .deb packages for every release, and the official install script picks the right one for your distribution and installs it with apt. Download the script first so you can read what it does before running it:
curl -fsSL https://code-server.dev/install.sh -o install-code-server.sh
less install-code-server.sh
You can preview the actions it will take without changing anything using --dry-run:
sh install-code-server.sh --dry-run
When you are happy with it, run the installation:
sh install-code-server.sh
The script downloads the latest release from GitHub, installs it with sudo, and prints how to start the service. Confirm that the binary is available:
code-server --version
4.x.x 1a2b3c4d... with Code 1.x.x
The exact version numbers will differ. The package also installs a systemd template unit, [email protected], that runs code-server as the user named after the @.
Step 2 - Starting code-server as a systemd service
Enable and start the service for your user. The $USER variable expands to your current username:
sudo systemctl enable --now code-server@$USER
Check that it is running:
systemctl status code-server@$USER
● [email protected] - code-server
Loaded: loaded (/usr/lib/systemd/system/[email protected]; enabled; preset: enabled)
Active: active (running) since ...
On first start code-server creates its configuration file at ~/.config/code-server/config.yaml with a random password. Confirm that it is listening only on the loopback interface:
ss -tlnp | grep 8080
LISTEN 0 511 127.0.0.1:8080 0.0.0.0:* users:(("node",pid=1234,fd=20))
The address 127.0.0.1:8080 means code-server is not reachable from outside the server. Nginx will be the only public entry point.
Step 3 - Setting a hashed password
The generated config stores the password in plain text. code-server also accepts a hashed-password key with an Argon2 hash, so the real password never sits on disk. Install the argon2 command line tool:
sudo apt update
sudo apt install -y argon2
Generate a hash. Replace your_strong_password with a long, unique password; the leading space keeps the command out of your shell history in the default Ubuntu Bash configuration:
echo -n 'your_strong_password' | argon2 "$(openssl rand -base64 16)" -e
$argon2i$v=19$m=4096,t=3,p=1$Zm9vYmFyYmF6cXV4$...
Now open the configuration file:
nano ~/.config/code-server/config.yaml
Replace its contents with the following, pasting your hash inside single quotes (the $ characters must not be interpreted):
bind-addr: 127.0.0.1:8080
auth: password
hashed-password: '$argon2i$v=19$m=4096,t=3,p=1$Zm9vYmFyYmF6cXV4$...'
cert: false
Each key does the following:
bind-addrkeeps code-server on localhost, behind Nginx.auth: passwordenables the login page.hashed-passwordreplaces the plainpasswordkey. Do not keep both.cert: falseleaves TLS to Nginx.
Restart the service to apply the change:
sudo systemctl restart code-server@$USER
If the service fails to start, journalctl -u code-server@$USER -n 50 shows the reason, usually a YAML syntax error.
Step 4 - Configuring Nginx as a reverse proxy
code-server relies on WebSockets for the editor, the terminal and file watching, so the proxy must pass the Upgrade and Connection headers. Create a server block for your subdomain:
sudo nano /etc/nginx/sites-available/code-server
Add the following configuration, replacing code.your_domain with your subdomain:
server {
listen 80;
listen [::]:80;
server_name code.your_domain;
location / {
proxy_pass http://127.0.0.1:8080/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
proxy_set_header Accept-Encoding gzip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 1h;
}
}
proxy_http_version 1.1 is required for WebSocket upgrades, and the one hour proxy_read_timeout stops Nginx from closing idle terminal sessions after the default 60 seconds.
Enable the site, test the syntax and reload Nginx:
sudo ln -s /etc/nginx/sites-available/code-server /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
At this point http://code.your_domain shows the code-server login page, but over plain HTTP. Do not log in yet.
Step 5 - Securing code-server with Let's Encrypt
Install Certbot and its Nginx plugin from the Ubuntu repositories:
sudo apt install -y certbot python3-certbot-nginx
Request a certificate. Certbot finds the matching server_name, adds the TLS configuration and sets up a redirect from HTTP to HTTPS:
sudo certbot --nginx -d code.your_domain
Enter an email address for expiry notices and accept the terms when prompted. When it finishes you will see:
Successfully deployed certificate for code.your_domain to /etc/nginx/sites-enabled/code-server
Congratulations! You have successfully enabled HTTPS on https://code.your_domain
The Ubuntu package installs a systemd timer that renews certificates automatically. Test the renewal process:
sudo certbot renew --dry-run
Step 6 - Logging in and verifying the setup
Open https://code.your_domain in your browser and log in with the password you hashed in Step 3. You should see the VS Code welcome screen.
To confirm that WebSockets work through the proxy, open the integrated terminal with Ctrl+` (or Terminal > New Terminal from the menu) and run:
whoami
The terminal should print your username. If the terminal stays blank or the editor keeps reconnecting, see the troubleshooting section below.
Finally, make sure port 8080 is not reachable from outside. From your local machine:
curl -m 5 http://your_server_ip:8080
curl: (28) Connection timed out after 5001 milliseconds
A timeout or "connection refused" is the expected result.
Step 7 - Installing extensions
code-server uses the Open VSX registry, not the Microsoft Marketplace, because Microsoft's license only allows the Marketplace in its own products. Most popular extensions (Python, Go, Prettier, ESLint, GitLens) are published there. You can install them from the Extensions view in the browser or from the server shell:
code-server --install-extension ms-python.python
code-server --install-extension esbenp.prettier-vscode
List what is installed:
code-server --list-extensions
esbenp.prettier-vscode
ms-python.python
If an extension is not on Open VSX, download its .vsix file on your computer and upload it to the server, then install it from the file:
code-server --install-extension ~/extension-name.vsix
Extensions are installed per user in ~/.local/share/code-server/extensions, so they survive code-server upgrades.
Upgrading code-server
To update, download and run the install script again as in Step 1. It installs the newest package over the existing one. Then restart the service:
sudo systemctl restart code-server@$USER
Your settings, extensions and configuration file are kept.
Troubleshooting
The editor loads, but the terminal is empty or shows "Reconnecting". WebSocket upgrades are not reaching code-server. Check that the location block contains proxy_http_version 1.1, Upgrade and Connection headers, and that Certbot did not create a second server block without them. If you use Cloudflare in proxied mode, WebSockets must be enabled for the zone.
Nginx returns "502 Bad Gateway". code-server is not running or listens on a different port. Check systemctl status code-server@$USER and compare bind-addr in config.yaml with the proxy_pass port.
The login page rejects the correct password. Make sure the hash is wrapped in single quotes, that the plain password key was removed, and that you restarted the service after editing the file.
The server runs out of memory. Extensions and language servers can use several gigabytes on large projects. You can cap code-server with a systemd drop-in:
sudo systemctl edit code-server@$USER
Add the following in the editor that opens, then save:
[Service]
MemoryMax=2G
Restart the service to apply it. If code-server keeps hitting the limit, resize your server instead.
Conclusion
You now have VS Code running on your Ubuntu 24.04 server, available at https://code.your_domain with TLS and a hashed password, and managed by systemd. As next steps, you can clone your repositories into your home directory and add an SSH key for Git, install the toolchains your projects need (Node.js, Python, Go) directly on the server, or add a second authentication layer such as an SSO proxy in front of Nginx if several people will use the instance.
