How to Build a Remote Development VM with code-server and Tailscale

Daniel MercerDaniel MercerSenior Systems Engineer
Intermediate
Updated Mar 27, 202614 min read~25 total
Ubuntu
code-server
Tailscale
DevOps
Security
Remote Development

On This Page

Prerequisites

A Raff Ubuntu 24.04 VM with at least 2 vCPU and 4 GB RAM (CPU-Optimized Tier 3 or higher), SSH access configured, a non-root user with sudo privileges, a Tailscale account or tailnet you can join from your laptop

Don't have a server yet? Deploy a Raff VM in 60 seconds.

Deploy a VM

Introduction

A remote development VM is a cloud server configured as your personal coding workspace, and code-server gives you a browser-based VS Code environment that runs entirely on that server. By combining code-server with Tailscale on a Raff Ubuntu 24.04 VM, you can build a private, portable development environment that you can open from any device on your tailnet without exposing your editor to the public internet.

This pattern is useful when you want a consistent Linux development box, better battery life on your laptop, or a safe way to work from multiple devices without copying projects around. It is also a strong fit for teams and solo developers who want cloud-based development without jumping immediately to a heavier platform layer. Raff’s hourly billing and unmetered bandwidth make this kind of environment practical to keep around for regular work or spin up only when you need it.

In this tutorial, you will prepare a fresh Ubuntu 24.04 VM, install code-server, lock it to localhost, install Tailscale, publish code-server privately to your tailnet with Tailscale Serve, and verify the setup from a second device. By the end, you will have a secure remote development VM that feels like a private cloud IDE rather than a public web app.

Note

This tutorial intentionally keeps code-server off the public internet. If you want a public setup with a domain name, use a reverse proxy and HTTPS instead. For that model, see /learn/tutorials/install-nginx-ubuntu-24-04.

Step 1 --- Prepare the Server for Remote Development

Before you install anything, make sure the server is updated and ready for day-to-day development work. You do not need a large stack for this setup, but you do want a stable baseline: current packages, Git, and basic utilities you will likely use from inside the remote IDE.

Run the following commands over SSH:

bashsudo apt update
sudo apt upgrade -y
sudo apt install -y git curl wget unzip build-essential ca-certificates

This updates the package index, applies available upgrades, and installs a small set of tools that cover most development workflows. git handles repository cloning and version control, curl and wget help with downloads and API testing, unzip is useful for archives, and build-essential gives you common compilers and headers needed by many language ecosystems.

You can verify the baseline with:

bashgit --version
curl --version | head -n 1

If both commands return version information without errors, the system is ready for the next step.

Tip

If this VM is fresh, complete the initial hardening steps before you use it for daily work. Raff already has a full guide for that at /learn/tutorials/secure-ubuntu-24-04-server and a focused firewall tutorial at /learn/tutorials/set-up-ufw-firewall-ubuntu-24-04.

Step 2 --- Install code-server and Start the Service

code-server runs VS Code on your server and serves the editor in a web browser. On Linux, the simplest official installation method is the upstream install script, which uses the system package manager when appropriate.

Install code-server:

bashcurl -fsSL https://code-server.dev/install.sh | sh
sudo systemctl enable --now code-server@$USER

The first command downloads and runs the official installer. On Ubuntu, that installer uses the supported package flow and sets up the service files for you. The second command enables the user-scoped code-server systemd service and starts it immediately.

Now check that the service is running:

bashsystemctl --user status code-server

If your shell session does not have access to the user service manager, this alternative also works:

bashsudo systemctl status code-server@$USER

You should see active (running) in the status output.

By default, code-server listens on 127.0.0.1:8080, uses password authentication, and stores its config in ~/.config/code-server/config.yaml. That default is good for this tutorial because we want to keep the editor local to the VM and publish it privately through Tailscale rather than exposing it directly.

Step 3 --- Configure code-server for Private Access

The default config works, but you should replace the generated password with a strong value that you control and store safely. In this setup, code-server remains bound to localhost, which means it is not reachable directly from the public network.

Generate a strong password, write a clean config, and restart the service:

bashPASSWORD=$(openssl rand -base64 24)

mkdir -p ~/.config/code-server

cat > ~/.config/code-server/config.yaml <<EOF
bind-addr: 127.0.0.1:8080
auth: password
password: ${PASSWORD}
cert: false
EOF

printf '%s\n' "$PASSWORD" > ~/.config/code-server/password.txt
chmod 600 ~/.config/code-server/password.txt

sudo systemctl restart code-server@$USER

This does four useful things:

  • keeps code-server bound to 127.0.0.1:8080
  • preserves password authentication
  • replaces the auto-generated secret with a strong random password
  • stores the password in a local file with restrictive permissions so you can retrieve it later

Confirm the config is in place:

bashcat ~/.config/code-server/config.yaml
cat ~/.config/code-server/password.txt

You should see bind-addr: 127.0.0.1:8080 and a long random password.

Now verify that code-server is responding locally on the VM:

bashcurl -I http://127.0.0.1:8080

A successful response usually returns an HTTP status line and headers. That tells you the editor is running correctly, even though it is not yet reachable from your laptop.

Step 4 --- Install Tailscale and Join Your Tailnet

Tailscale gives you a private encrypted network between your devices. Instead of opening port 8080 publicly, you will install Tailscale on the VM, join it to your tailnet, and let Tailscale handle private routing.

Install Tailscale and bring the node online:

bashcurl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

After you run sudo tailscale up, Tailscale prints an authentication URL. Open that URL in your browser, sign in to your Tailscale account, and authorize the machine to join your tailnet.

Once authentication is complete, confirm the node is connected:

bashtailscale ip -4
tailscale status

The first command prints the VM’s Tailscale IPv4 address. The second shows connected peers and status information.

At this point, the VM is part of your private mesh network, but code-server is still only reachable on 127.0.0.1:8080 from the server itself. The next step is what turns that local-only editor into a private HTTPS service for the rest of your tailnet.

Step 5 --- Publish code-server Privately with Tailscale Serve

Tailscale Serve lets you share a local service securely with devices in your tailnet. This is exactly what we want here: code-server stays local on 127.0.0.1:8080, while Tailscale publishes it privately over HTTPS.

Run:

bashsudo tailscale serve --bg localhost:8080
sudo tailscale serve status

The first command creates a persistent HTTPS reverse proxy from your tailnet URL to localhost:8080. The --bg flag keeps the configuration active across reboots and Tailscale restarts. The second command shows the active Serve configuration and the URL you should use to access it.

The output will show a Tailscale HTTPS endpoint for this machine, usually based on the device’s MagicDNS name. Open that URL from a second device that is also signed into the same tailnet.

When the login page appears, use the password stored in:

bashcat ~/.config/code-server/password.txt

If the page does not load from your laptop or phone, check these common issues:

  • the client device is not signed into the same tailnet
  • Tailscale Serve was not enabled because your tailnet needs HTTPS certificates enabled
  • code-server is not running
  • the VM has not finished joining the tailnet

You can recheck all three moving parts with:

bashsudo systemctl status code-server@$USER
tailscale status
sudo tailscale serve status

If all three are healthy, you should be able to sign in to the browser IDE from any Tailscale-connected device.

Tip

This setup is especially useful on tablets, thin laptops, or secondary machines. Your files, package installs, and compute stay on the VM, while your browser becomes the client.

Step 6 --- Create a Workspace and Make the VM Useful

Once you can log in, turn the VM into a practical workspace. A remote editor is only useful if it opens the right directories, has Git access, and can run your day-to-day commands.

Create a projects directory and clone a repository:

bashmkdir -p ~/projects
cd ~/projects
git clone https://github.com/octocat/Hello-World.git

Now open ~/projects/Hello-World from the code-server file picker.

If you want a cleaner default startup path, create a dedicated workspace folder for your own projects and keep each repository under ~/projects. This keeps the remote VM tidy and makes it easier to back up later with snapshots or automated backups.

You can also install extensions from inside code-server just as you would in desktop VS Code. For command-line installation, use:

bashmkdir -p ~/projects
cd ~/projects
git clone https://github.com/octocat/Hello-World.git

Now open ~/projects/Hello-World from the code-server file picker.

If you want a cleaner default startup path, create a dedicated workspace folder for your own projects and keep each repository under ~/projects. This keeps the remote VM tidy and makes it easier to back up later with snapshots or automated backups.

You can also install extensions from inside code-server just as you would in desktop VS Code. For command-line installation, use:

bashcode-server --install-extension ms-python.python

Replace the extension ID with whichever toolchain you need. A few practical examples:

  • ms-python.python for Python
  • golang.go for Go
  • rust-lang.rust-analyzer for Rust
  • ms-vscode.cpptools for C/C++
  • esbenp.prettier-vscode for formatting JavaScript and TypeScript

Because this is a remote Linux machine, you can now use the browser editor and terminal together on the same server. That means no sync gaps between your editor and runtime environment.

Step 7 --- Verify the Full Remote Development Workflow

The last step is to verify the end-to-end workflow, not just the installation.

Check these items:

From the server

  • code-server is running
  • Tailscale is connected
  • Tailscale Serve is active

From your laptop or second device

  • the Tailscale HTTPS URL opens in the browser
  • the code-server login page appears
  • you can sign in successfully
  • you can open a project folder
  • the integrated terminal works

For security

  • port 8080 is not open publicly
  • your SSH access still works
  • the editor stays private to your tailnet

Run these checks on the server:

bashsudo systemctl is-active code-server@$USER
tailscale ip -4
sudo tailscale serve status
sudo ss -tulpn | grep 8080

You want to see:

  • active from the systemd check
  • a valid Tailscale IP
  • an active Serve rule pointing at localhost:8080
  • code-server listening on local loopback, not a public bind address

If you are using UFW, you do not need to open 8080 publicly for this workflow. Only your existing SSH rule should be required, plus any other services you intentionally expose. That is one reason this design is cleaner than publishing the editor on the public web.

For rollback, you can disable the private published endpoint with:

bashsudo tailscale serve reset

To stop and disable code-server entirely:

bashsudo systemctl disable --now code-server@$USER

To remove code-server if you installed it from the package flow:

bashsudo apt remove -y code-server

Conclusion

You have built a remote development VM on Raff using Ubuntu 24.04, code-server, and Tailscale. code-server gives you a full browser-based VS Code experience on the server, while Tailscale keeps access private and encrypted inside your tailnet instead of forcing you to expose your editor publicly.

This setup gives you a practical development environment that works from multiple devices, preserves a consistent Linux workspace, and reduces the risk of accidentally turning your IDE into another public-facing application. It is a strong fit for solo developers, contractors, founders, and small teams who want a simple remote dev box before moving to a larger platform approach.

From here, you can:

This tutorial was tested against current upstream install methods and a live Ubuntu 24.04 workflow on a Raff Tier 3 VM.

Get notified when we publish new tutorials

Cloud tips, step-by-step guides, and infrastructure insights — straight to your inbox.

Frequently Asked Questions

Ready to get started?

Deploy an Ubuntu 24.04 VM and follow along in under 60 seconds.

Deploy a VM Now

Related Articles