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 sudo privileges. 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 Full profile.
  • A domain or subdomain, for example code.your_domain, with a DNS A record pointing to your_server_ip. Let's Encrypt needs this record to issue the certificate.

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-addr keeps code-server on localhost, behind Nginx.
  • auth: password enables the login page.
  • hashed-password replaces the plain password key. Do not keep both.
  • cert: false leaves 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.