I wanted VS Code on every device without installing VS Code on every device.
That’s the pitch for code-server—the open-source project that runs VS Code in a browser. Your code lives on a server. Your terminal sessions live on a server. Your extensions, settings, everything—server-side. Any device with a web browser becomes a workstation.
The installation took 10 minutes. The authentication layer took three hours. Here’s both.
Why Browser-Based IDE?
The use case is simple: I have computers everywhere. Desktop at home. Laptop for travel. Sometimes I’m on a family member’s machine helping with something and think “if only I could just check this one file.”
GitHub Codespaces does this with paid compute. VS Code Remote-SSH does this but requires VS Code installed locally. code-server does this with nothing but a browser and a server I already own.
The server runs 24/7 anyway. It has plenty of RAM. Why not put VS Code on it?
The Installation
code-server has a one-liner installer:
curl -fsSL https://code-server.dev/install.sh | sh
This detects your OS, downloads the appropriate package, and installs it. On my server (Debian-based), it installed the code-server binary to /usr/bin/code-server and created a systemd service file.
The default configuration lives at ~/.config/code-server/config.yaml:
bind-addr: 127.0.0.1:8080
auth: password
password: your-password-here
cert: false
Out of the box, it binds to localhost on port 8080 with password authentication. For local testing, this works. Start it with:
code-server
Open http://localhost:8080, enter your password, and you’re in VS Code. Done.
But localhost isn’t useful when I’m on a different network. I needed external access with proper authentication.
The Authentication Problem
Exposing code-server to the internet means anyone who can reach your server can try to access your development environment. The built-in password authentication works but has limitations:
- Single password for everyone: No user differentiation
- No SSO: Every service has its own password
- No audit trail: No way to know who logged in when
- Brute force vulnerable: Unless you add rate limiting yourself
I wanted SSO. Specifically, I wanted Cloudflare Access—their zero-trust authentication layer that puts authentication in front of any web application. Eventually I’ll migrate to Keycloak for full identity management, but Cloudflare Access is already set up for other services.
Setting Up Cloudflare Access
The key insight: when you put Cloudflare Access in front of an application, you can disable the application’s built-in authentication entirely. Cloudflare handles who can reach the service; the service assumes anyone who reaches it is authorized.
First, I changed the code-server config:
bind-addr: 127.0.0.1:8080
auth: none # Cloudflare handles auth
cert: false
Setting auth: none removes code-server’s login page. Anyone who can reach port 8080 gets direct access to VS Code. This is only safe because Cloudflare Access prevents anyone from reaching port 8080 without authentication.
The Nginx Layer
code-server binds to localhost. Cloudflare needs to reach it from the internet. Nginx bridges the gap as a reverse proxy:
server {
listen 80;
server_name code.myserver.com;
# Trust Cloudflare's IP ranges
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
# ... (more Cloudflare IP ranges)
real_ip_header CF-Connecting-IP;
location / {
# Forward Cloudflare Access headers
proxy_set_header CF-Access-Authenticated-User-Email $http_cf_access_authenticated_user_email;
proxy_set_header CF-Access-JWT-Assertion $http_cf_access_jwt_assertion;
# Standard proxy settings
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
proxy_set_header Accept-Encoding gzip;
}
}
The set_real_ip_from directives tell Nginx to trust Cloudflare’s proxy headers for the real client IP. The CF-Access-Authenticated-User-Email header contains the email address of whoever authenticated—useful if the application wants to know who’s using it.
The critical proxy headers for code-server are Upgrade and Connection. code-server uses WebSockets for real-time editor communication. Without these headers, you get a static page that can’t send keystrokes to the server.
Cloudflare Zero Trust Configuration
In the Cloudflare Zero Trust dashboard:
- Access → Applications → Add Application → Self-hosted
- Application domain:
code.myserver.com - Session duration: Whatever makes sense for your workflow
The policy determines who can access the application:
- For personal use: Add your email address to an “Emails” rule
- For team use: Configure an identity provider (Google Workspace, GitHub, Okta)
Once configured, any request to code.myserver.com goes through Cloudflare’s edge network. Unauthenticated requests get redirected to a login page. Authenticated requests get proxied to your origin server with identity headers attached.
Running as a Service
To keep code-server running across reboots:
sudo systemctl enable --now code-server@$USER
Or if you installed manually, create a systemd service:
sudo tee /etc/systemd/system/code-server.service > /dev/null << 'EOF'
[Unit]
Description=code-server
After=network.target
[Service]
Type=simple
User=commander
ExecStart=/usr/bin/code-server --config /home/commander/.config/code-server/config.yaml
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now code-server
Systemd handles restarts if it crashes.
Persistent Storage Considerations
Everything code-server does happens on the server. That includes:
- Workspace files: Wherever you open folders from
- Extensions: Installed to
~/.local/share/code-server/extensions - Settings: Synced to
~/.local/share/code-server/User/settings.json - Terminal history: Server-side shell history
This is the whole point—but it also means backups matter more. A disk failure loses your development environment, not just a cached editor state. I added the code-server directories to my backup schedule.
What Works Well
Extension support is excellent. Almost every VS Code extension works. Language servers, formatters, themes—they all run server-side and communicate via WebSocket to the browser.
Terminal integration is seamless. The integrated terminal runs real server shells. SSH keys, installed tools, everything is there. I can run builds, manage Docker containers, whatever I’d do with a local terminal.
Git integration works. The Source Control panel, git commands in terminal, GitHub extensions—all functional. The server has my SSH keys configured, so pushing and pulling just works.
Settings sync is unnecessary. Since settings live on the server, there’s nothing to sync. I configure once and it’s configured everywhere.
What’s Slightly Awkward
Latency is noticeable. Keystrokes go to Cloudflare, to my server, process, return via Cloudflare, render in browser. On good connections it’s fine. On cellular or slow WiFi, there’s perceptible lag.
Browser shortcuts conflict. Ctrl+W closes browser tabs. Ctrl+N opens browser windows. code-server tries to intercept these, but it doesn’t always win. I’ve retrained my muscle memory to use Ctrl+Shift combinations.
File uploads are clunky. Dragging files into the browser editor works, but large files are slow. For substantial file transfers, I use rsync or SFTP separately.
The Final Architecture
User Browser
↓
Cloudflare Edge (Authentication + WAF)
↓
Cloudflare Tunnel / Direct Connection
↓
Nginx Reverse Proxy (localhost)
↓
code-server (localhost:8080)
↓
Server Filesystem
Every layer adds something:
- Cloudflare: Authentication, SSL termination, DDoS protection
- Nginx: WebSocket proxying, header forwarding, IP trust
- code-server: The actual IDE
Requests flow down; code flows… everywhere. That’s the point.
Was It Worth It?
Ten minutes for installation. Three hours for authentication. One evening of configuration testing.
Now I can write code from any browser. No local VS Code install. No extension sync issues. No “let me just clone this on my laptop” delays.
The laptop can be a Chromebook. The “device” can be an iPad with a keyboard. The emergency terminal can be a phone. As long as it has a browser and internet, it has my development environment.
That’s worth an evening of configuration.
The Cloudflare Access policy lets me revoke my own access if needed. I’ve tested this exactly once. It was informative.