---
title: "Deploy Ubuntu Claude Code Desktop (GUI)"
description: "Ubuntu desktop in your browser with VS Code, Chrome and Claude Code ready."
category: "Other"
url: https://railway.com/deploy/claude-code-desktop
---

# Deploy Ubuntu Claude Code Desktop (GUI)

Ubuntu desktop in your browser with VS Code, Chrome and Claude Code ready.

**[Deploy Ubuntu Claude Code Desktop (GUI) on Railway](https://railway.com/template/claude-code-desktop)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/claude-code-desktop/manifest.json

- **Creator:** darseen
- **Category:** Other

## Template content

### desktop https://devicons.railway.com/i/ubuntu.svg

- **Image:** kasmweb/ubuntu-noble-desktop:1.19.0-rolling-weekly
- **Start command:** `bash -c 'set -e
[ -n "${ANTHROPIC_API_KEY:-}" ] || unset ANTHROPIC_API_KEY
if [ ${#VNC_PW} -lt 6 ]; then echo "FATAL: VNC_PW must be at least 6 characters (KasmVNC requirement). Set a longer password and redeploy." >&2; exit 1; fi
echo "kasm-user ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/kasm-user
chmod 0440 /etc/sudoers.d/kasm-user
chown 1000:1000 /home/kasm-user
if [ ! -f /home/kasm-user/.railway-owned ]; then chown -R 1000:1000 /home/kasm-user; touch /home/kasm-user/.railway-owned; chown 1000:1000 /home/kasm-user/.railway-owned; fi
sed -i "s|function wait_for_network_devices() {|function wait_for_network_devices() { return 0;|" /dockerstartup/vnc_startup.sh
grep -q "wait_for_network_devices() { return 0;" /dockerstartup/vnc_startup.sh || { echo FATAL: Kasm startup changed, wait_for_network_devices patch no longer applies >&2; exit 1; }
ln -sf /home/kasm-user/.local/bin/claude /usr/local/bin/claude
for b in node npm npx corepack; do ln -sf /home/kasm-user/.local/node/bin/$b /usr/local/bin/$b; done
cat > /dockerstartup/provision.sh <<"PROVEOF"
set -u
export HOME=/home/kasm-user
cd $HOME
for i in $(seq 1 60); do [ -f $HOME/.bashrc ] && break; sleep 1; done
log() { echo "[provision] $*"; }
NODE_DIR=$HOME/.local/node
if [ ! -x $NODE_DIR/bin/node ]; then
  log installing Node.js 24 LTS
  TARBALL=$(curl -fsSL https://nodejs.org/dist/latest-v24.x/SHASUMS256.txt | grep -o "node-v[0-9.]*-linux-x64.tar.xz" | head -1)
  mkdir -p $NODE_DIR
  curl -fsSL "https://nodejs.org/dist/latest-v24.x/$TARBALL" | tar -xJ -C $NODE_DIR --strip-components=1 || log Node.js install failed
fi
if [ ! -x $HOME/.local/bin/claude ]; then
  log installing Claude Code
  curl -fsSL https://claude.ai/install.sh | bash || log Claude Code install failed
fi
grep -q "local/node/bin" $HOME/.bashrc || echo "export PATH=\$HOME/.local/bin:\$HOME/.local/node/bin:\$PATH" >> $HOME/.bashrc
if ! ls $HOME/.vscode/extensions 2>/dev/null | grep -q anthropic.claude-code; then
  log installing the Claude Code VS Code extension
  code --no-sandbox --user-data-dir $HOME/.config/Code --install-extension anthropic.claude-code || log VS Code extension install failed
fi
if [ ! -f $HOME/.config/Code/User/settings.json ]; then
  mkdir -p $HOME/.config/Code/User
  cat > $HOME/.config/Code/User/settings.json <<"JSONEOF"
{
  "chat.disableAIFeatures": true,
  "workbench.startupEditor": "none",
  "telemetry.telemetryLevel": "off",
  "update.mode": "none"
}
JSONEOF
fi
if [ ! -f $HOME/Desktop/claude-code.desktop ]; then
  mkdir -p $HOME/.local/share/icons $HOME/Desktop
  curl -fsSL https://cdn.simpleicons.org/claude/D97757 -o $HOME/.local/share/icons/claude.svg || true
  ICON=utilities-terminal
  [ -s $HOME/.local/share/icons/claude.svg ] && ICON=$HOME/.local/share/icons/claude.svg
  cat > $HOME/Desktop/claude-code.desktop <<DESKEOF
[Desktop Entry]
Type=Application
Name=Claude Code
Comment=Open a terminal running Claude Code
Exec=xfce4-terminal --title="Claude Code" --command=claude
Icon=$ICON
Terminal=false
Categories=Development;
DESKEOF
  chmod +x $HOME/Desktop/claude-code.desktop
fi
for i in $(seq 1 120); do SESS=$(pgrep -u 1000 -x xfce4-session | head -1); [ -n "$SESS" ] && break; sleep 1; done
if [ -n "$SESS" ]; then
  export $(tr "\0" "\n" < /proc/$SESS/environ | grep ^DBUS_SESSION_BUS_ADDRESS=)
  gio set -t string $HOME/Desktop/claude-code.desktop metadata::xfce-exe-checksum "$(sha256sum $HOME/Desktop/claude-code.desktop | cut -d" " -f1)" || log could not mark the launcher trusted
fi
log done
PROVEOF
chmod 755 /dockerstartup/provision.sh
setpriv --reuid=1000 --regid=1000 --init-groups bash -c "bash /dockerstartup/provision.sh > /home/kasm-user/.provision.log 2>&1" &
exec setpriv --reuid=1000 --regid=1000 --init-groups /dockerstartup/kasm_default_profile.sh /dockerstartup/vnc_startup.sh /dockerstartup/kasm_startup.sh --tail-log'`

### proxy https://github.com/caddyserver.png?size=460

- **Image:** caddy:2-alpine
- **Start command:** `sh -c 'printf "%s" "$CADDYFILE" > /etc/caddy/Caddyfile && exec caddy run --config /etc/caddy/Caddyfile --adapter caddyfile'`
- **Health check:** /proxy-healthz
- **Public domain:** Yes

## Documentation

# Deploy and Host Ubuntu Claude Code Desktop (GUI) on Railway

![Ubuntu Claude Code Desktop (GUI) running in the browser, with the Claude Code launcher on the desktop](https://vaze.up.railway.app/api/hosting/railway-templates/claude-code-desktop/claude-code-desktop.png)

![Claude Code opened from its desktop icon](https://vaze.up.railway.app/api/hosting/railway-templates/claude-code-desktop/claude-code-desktop-terminal.png)

Ubuntu Claude Code Desktop (GUI) is a full Ubuntu 24.04 XFCE desktop that runs in your
browser with Claude Code, VS Code, Chrome and Node.js already set up. It is the
graphical version of the "Ubuntu + Claude Code over SSH" boxes: the same cloud
dev machine, but with a real editor, a real browser for OAuth logins and
previewing what you build, and no SSH key or VNC client.

## About Hosting Ubuntu Claude Code Desktop (GUI)

Deploying runs two services: the desktop itself, and a small Caddy proxy that
is the only public surface. The desktop image is Kasm's Ubuntu Noble workspace
(about 2.6 GB compressed), which already ships VS Code, Chrome, Chromium,
Firefox, git, tmux and Python. On first boot a background job installs Node.js
24 LTS, the Claude Code CLI and the Claude Code VS Code extension into the home
directory, which lives on a Railway volume, so later boots skip the download.
The login page appears within a minute or two; the tools finish installing
shortly after, and `~/.provision.log` shows their progress.

Cost is the thing to plan for. A graphical desktop idles near 1 GB of RAM with
a session attached, and more with Chrome and VS Code open. Enabling Railway's
app sleep is strongly recommended so the desktop suspends when you close the tab.

## Common Use Cases

- A persistent Claude Code workstation reachable from a tablet, a Chromebook or a locked-down work laptop
- Letting Claude Code run long agentic tasks on a machine that stays up after you close your laptop
- A disposable, isolated box for trying Claude Code on an untrusted repo, away from your own credentials and files

## Dependencies for Ubuntu Claude Code Desktop (GUI) Hosting

- A Railway volume mounted at `/home/kasm-user`, which holds the desktop's home directory, the installed tools and Claude Code's login. Without it, everything is lost on redeploy.
- A Claude account (Pro, Max, Team or Enterprise) or an Anthropic API key. Sign in from the desktop's terminal with `claude` on first use, or set `ANTHROPIC_API_KEY` at deploy time to skip the browser login.
- No GitHub repo backs this template. Both services boot from public images.

### Deployment Dependencies

- [kasmweb/ubuntu-noble-desktop](https://hub.docker.com/r/kasmweb/ubuntu-noble-desktop) - the upstream desktop image
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) - installed with Anthropic's native installer
- [Claude Code for VS Code](https://marketplace.visualstudio.com/items?itemName=anthropic.claude-code) - the editor extension
- [Node.js](https://nodejs.org/) - 24 LTS, installed as a tarball into the home directory
- [KasmVNC](https://github.com/kasmtech/KasmVNC) - the streaming layer
- [Caddy](https://caddyserver.com/) - the TLS-terminating proxy

### Implementation Details

**Signing in.** Double-click the Claude Code icon on the desktop, or open a
terminal and run `claude`. It opens the sign-in page in Chrome inside the
desktop; log in with your Claude account and the CLI is authenticated. The
session is stored under `~/.claude` on the volume, so you sign in once. If you set `ANTHROPIC_API_KEY` instead, the CLI uses it
directly and bills the API; leave it blank to use a Claude subscription. An
empty key is unset at boot so it can never shadow the browser login.

**Where the tools live.** Node.js is unpacked to `~/.local/node`, Claude Code to
`~/.local/bin/claude`, and the VS Code extension to `~/.vscode/extensions`. All
three are on the volume. The boot script links `node`, `npm`, `npx` and `claude`
into `/usr/local/bin` so terminals and VS Code find them without any shell
profile, and appends both directories to `~/.bashrc` so `npm install -g` binaries
are found too. Claude Code updates itself in place from then on.

**VS Code.** The Claude Code extension is installed, and the built-in Copilot
chat is turned off in the seeded `settings.json` so Claude Code is the only
assistant in the editor. The extension does not run in Restricted Mode, so when
you open a folder for the first time, choose "Trust" in VS Code's workspace
prompt and the Claude Code icon appears in the activity bar.

**Why there is a proxy service.** Kasm's startup script passes `-sslOnly` to
`vncserver`, so the desktop always serves HTTPS on 6901 and the documented
`network.ssl.require_ssl: false` setting is ignored. Railway speaks plain HTTP
to containers, so Caddy accepts Railway's HTTP and re-encrypts to Kasm,
skipping verification because the certificate is self-signed and regenerated on
every boot. Only the proxy has a public domain.

**`VNC_PW` has a 6-character minimum.** KasmVNC's password tool silently
rejects anything shorter, which would leave the desktop unable to start and the
URL serving 502. The template validates the length at boot and fails with an
explicit message instead.

**Passwordless sudo is enabled deliberately.** Kasm ships `kasm-user` with a
locked password and no sudo rights, so a stock container cannot `apt install`
anything. A dev box needs that, so the template grants passwordless sudo at
boot. To lock it back down, remove `/etc/sudoers.d/kasm-user` inside the desktop.

**Security model.** `VNC_PW` is the only thing between the public Railway URL
and a root-capable desktop holding your Claude login. Set a strong one. Anyone
who reaches the URL with it can run anything as you, including Claude Code
against your account.

## Why Deploy Ubuntu Claude Code Desktop (GUI) on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying Ubuntu Claude Code Desktop (GUI) on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.


## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/claude-code-desktop
