Running Claude Code safely on remote Linux
Field notes from a real deployment (DigitalOcean US droplet + browser remote desktop + Claude Desktop). Goal: use Claude Desktop / Claude Code from a browser on a Linux box you control in the US, with Chinese input, file upload, audio, and an HTTPS portal. Dated 2026-09-13 · Target OS: Ubuntu 24.04 LTS
0. What you end up with#
| Capability | Notes |
|---|---|
| US VPS | DigitalOcean Droplet, billed monthly |
| Graphical desktop | TigerVNC + XFCE (stable for Claude Desktop) / Plasma (optional) |
| Browser entry | Password portal at https://wi.ci (Cloudflare Tunnel; 3389/5901 stay closed) |
| Two remote paths | noVNC (VNC desktop) · Guacamole + audio (xrdp/RDP) |
| Claude | Claude Desktop on Linux, signed in; CLI / projects still work on the desktop |
| Chinese input | Fcitx5 Pinyin, toggle with Ctrl+Space |
| File transfer | Portal “Files” drop zone → desktop Inbox |
| Security baseline | Non-root daily user, key login, UFW, secrets never in the doc |
1. Why do it this way#
- Claude Desktop (and some of its features) is sensitive to network region. A US VPS plus a local browser is more controllable than an unstable direct connection from your laptop.
- Do not expose VNC/RDP ports to the public internet. Put the desktop behind HTTPS with Cloudflare Tunnel (or an SSH tunnel).
- Day-to-day work runs as a low-privilege user
desk. Use root only for packages and system config. - Passwords and tokens live in a secrets file on the server, or in a password manager on your laptop. Never put them in a tutorial repo or a copy of a chat that can be forwarded.
2. Buy and initialise DigitalOcean#
2.1 Sizing#
- Size: 2 vCPU / 4 GB RAM (Claude Desktop plus a browser is memory-hungry; smaller boxes OOM)
- OS: Ubuntu 24.04 LTS x64
- Region: United States (e.g. NYC3)
- Budget: around $24/month (subject to DigitalOcean’s pricing)
- Billing: register with an email; pay however your account allows (card / Alipay, etc.)
2.2 Create the Droplet#
- Sign in to DigitalOcean.
- Create → Droplets → pick region, Ubuntu 24.04, 2C/4G.
- Prefer SSH Key auth. If you must start with a password, switch to keys immediately and disable password login.
- Hostname example:
claude-code-us. - After create, record the public IP and any initial root password — only in an encrypted store on your machine.
2.3 First login from your laptop#
ssh root@YOUR_DROPLET_IP
Do this next:
apt update && apt -y upgrade
timedatectl set-timezone Asia/Shanghai # or keep UTC
adduser desk
usermod -aG sudo desk
## Give desk a login password (can match the VNC password so the keyring unlocks)
2.4 Harden SSH (strongly recommended)#
Generate a key locally if you do not have one, then install the public key:
## on your laptop
ssh-copy-id -i ~/.ssh/id_ed25519.pub desk@YOUR_DROPLET_IP
Suggested /etc/ssh/sshd_config on the server:
PermitRootLogin prohibit-password
PasswordAuthentication no
PubkeyAuthentication yes
Then:
systemctl reload ssh
UFW baseline (allow SSH first, then enable):
ufw allow OpenSSH
ufw enable
## With Cloudflare Tunnel you usually never open 5901/3389/8080 to the world
3. Graphical desktop and TigerVNC#
3.1 Desktop environment#
What actually worked:
- XFCE: light, stable, Claude Desktop windows render (use this as the noVNC default).
- KDE Plasma 5: nicer looking, but Electron (Claude Desktop) under VNC/Plasma has shown InputOnly / invisible windows. Keep it as a spare; do not make it the Claude daily driver.
Install example (you can install both and switch):
apt install -y xfce4 xfce4-goodies
## optional:
apt install -y kde-plasma-desktop
3.2 TigerVNC (loopback only)#
apt install -y tigervnc-standalone-server tigervnc-common
sudo -u desk vncpasswd # set the VNC password
Key points for ~desk/.vnc/xstartup:
unset DBUS_SESSION_BUS_ADDRESSthendbus-launch- Export IME environment (see section 8)
exec startxfce4(or Plasma’sstartplasma-x11)
Start (display :1 → port 5901):
sudo -u desk vncserver :1 -geometry 1920x1080 -depth 24
Security: VNC listens on localhost only. The public path is Tunnel / SSH forward.
ssh -L 5901:127.0.0.1:5901 desk@YOUR_DROPLET_IP
## then point a local VNC client at 127.0.0.1:5901
3.3 Prevent sleep / lock (required on a remote box)#
Otherwise the browser session suddenly “locks / goes black”:
systemd: masksleep.target suspend.target hibernate.target- Plasma: disable Autolock in
~/.config/kscreenlockerrc - Put
xset s off -dpmsin the session startup script
4. Install Claude Desktop (Linux)#
- Install Claude Desktop the way Anthropic currently documents for Linux (apt package or the current official method).
- Launch it as
deskon the graphical desktop and complete login. - A desktop shortcut
start-claude-desktop.shthat forcesDISPLAY=:1helps.
Caveats:
- 4 GB of RAM is tight: few Chrome tabs; do not stack several Electron apps.
- If it “starts but you cannot see the window” on Plasma: switch back to XFCE and launch Claude there.
- You can disable the KDE Wallet prompt (
kwalletrc→Enabled=false); Claude may fall back to plaintext session storage. That is a trade-off. - GNOME Keyring / login password matching the
deskpassword means fewer keyring interruptions.
Claude Code CLI (optional, complementary to Desktop):
## Install Node / the claude CLI from the current official docs
## Use it in a desktop terminal. Do not write API keys into world-readable files
5. Web remote-desktop portal (noVNC + upload)#
5.1 Components#
- websockify: turn
5901into a WebSocket - noVNC: VNC in the browser
- Small Flask portal: login, resolution, language, file upload, mode switch
- nginx: reverse-proxy local ports
- cloudflared: Cloudflare Tunnel → local nginx
Typical local ports (127.0.0.1 only):
| Service | Port |
|---|---|
| Portal Flask | 8081 |
| nginx (Tunnel target) | 8080 |
| VNC | 5901 |
| Guacamole Tomcat | 8085 |
| guacd | 4822 |
| xrdp | 3389 (localhost only) |
5.2 File upload (Inbox)#
- Server directory example:
/home/desk/Desktop/Inbox - The portal exposes drag-and-drop upload; the UI can be a floating “Files” panel on the right.
5.3 Cloudflare Tunnel + domain#
- Point a domain (example
wi.ci) at Cloudflare DNS. - Install
cloudflared, create a Tunnel, Ingress:
ingress:
- hostname: wi.ci
service: http://127.0.0.1:8080
- service: http_status:404
- Add a portal password of its own (better if it differs from the VNC password).
- Check:
https://wi.ci→ log in → noVNC shows the desktop.
6. Guacamole + xrdp (browser remote with audio)#
6.1 Why Guacamole as well#
- noVNC’s audio path is weak or needs an extra WebSocket.
- Guacamole speaks RDP (xrdp) and, with PipeWire /
xrdp-sink, can play sound in the browser.
Note: Guacamole/RDP is a separate desktop session (e.g. display :10), not the same noVNC screen.
6.2 Install notes#
- Install
xrdp/xorgxrdp;deskmust be allowed to log in. - Deploy Guacamole + guacd + PostgreSQL with Docker Compose.
- nginx reverse-proxies
/guacamole/and upgrades WebSocket. - UFW must allow the Docker bridge to reach host guacd:4822 (otherwise you sit on Waiting / Bad gateway):
ufw allow from 172.16.0.0/12 to any port 4822
- RDP audio: build/install
pulseaudio-module-xrdpor the PipeWire xrdp module; in the sessionPULSE_SINK=xrdp-sink. - Example Guacamole connection:
127.0.0.1:3389, userdesk,enable-audio=true,server-layout=en-us-qwerty.
6.3 How the portal connects#
- Dropdown: Guacamole+audio / noVNC
- The Guacamole iframe needs
tabindexplus a click to focus, or the portal steals keyboard events. - Get English typing working first, then turn on the Chinese IME (next section).
6.4 Paths that were removed#
- RustDesk: dropped to avoid extra ports and upkeep (your threat model may differ).
- A separate “RDP client” entry can be merged into Guacamole so users have fewer choices.
7. Resolution, language, and portal UX#
- Resolution dropdown: call
xrandr/ a script to change VNC geometry. - Language: portal UI zh/en toggle.
- Fullscreen, open in a new tab, log out.
- Favicon and a compact top bar, so the desktop keeps vertical space.
8. Chinese IME (Fcitx5 Pinyin)#
8.1 Install#
apt install -y fcitx5 fcitx5-chinese-addons fcitx5-frontend-gtk3 \
fcitx5-frontend-gtk4 fcitx5-frontend-qt5 fonts-noto-cjk fonts-noto-cjk-extra
8.2 Configuration rules#
- Default English keyboard,
ActiveByDefault=False. - Toggle: Ctrl + Space → Pinyin; again to return to English.
- Environment variables (in
~/.xsession, VNCxstartup,~/.profile):
export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS=@im=fcitx
- At session start:
fcitx5 -d --replace
8.3 Interaction with Guacamole#
- The IME process once swallowed every keypress → English stopped working too.
- Fix order: first make sure English works when Pinyin is off via Ctrl+Space, then teach the Chinese toggle.
- If English dies again:
Ctrl+Spaceback to English, or reconnect Guacamole.
9. Secure-operation checklist (treat as mandatory)#
- Do not open 5901 / 3389 / 8080 / 4822 on the public internet. Only 22 (or a moved SSH port) plus Cloudflare.
- SSH key login, password login off; root has no password login.
- Portal password is not a throwaway; better if it differs from VNC.
Secrets path examples (mode
600, readable only by root/desk):/root/secrets/desk-portal-password.txt/root/secrets/desk-rdp-password.txt/root/secrets/guacamole-admin.txt
- Regular
apt upgrade; watch Claude Desktop and kernel updates. - Snapshot: take a DigitalOcean Snapshot before large changes.
- Do not post cookies, API keys, or desktop recordings in public places.
- Memory alert:
free -h. On OOM, close browser tabs / restart the desktop session first.
10. Daily use#
- Open
https://your-domain→ enter the portal password. Pick a connection:
- noVNC: daily desktop, Claude Desktop (XFCE)
- Guacamole+audio: when you need system sound
- Click the desktop area so keyboard focus is remote.
- Chinese:
Ctrl+Space. - Files: portal “Files” → drop into Inbox.
- Leave: portal “Log out”. Closing the browser tab is not a lock screen.
Renewal reminder: put Droplet billing and the Claude subscription on a calendar / assistant reminder (e.g. a fixed day each month).
11. Troubleshooting#
| Symptom | Likely cause | Fix |
|---|---|---|
| Portal 502 | cloudflared / origin down | systemctl restart cloudflared nginx desk-portal |
| Guacamole Waiting / Bad gateway | UFW blocking Docker→4822 | Allow 172.16.0.0/12 → 4822 |
| Guacamole black screen | Plasma compositor / dead session | KWIN_COMPOSE=N; reconnect; or use noVNC |
| Keyboard dead | iframe unfocused / IME swallowing keys | Click desktop; Ctrl+Space; check fcitx |
| Claude started, invisible | Plasma + Electron | Switch to XFCE; clear Singleton lock; restart |
| Sudden lock screen | Plasma autolock / sleep | Disable Autolock; mask sleep targets |
| No sound | not using xrdp-sink / audio off | Guacamole mode + PipeWire xrdp module |
| Stutter / killed processes | 4G RAM full | Fewer tabs; restart the VNC session |
12. Switching desktops (notes)#
Keep switch scripts on the server (example paths):
/home/desk/bin/use-xfce— XFCE for noVNC (recommended for Claude)/home/desk/bin/use-plasma— back to Plasma (test Claude yourself)
After changing ~/.vnc/xstartup:
sudo -u desk vncserver -kill :1
sudo -u desk vncserver :1 -geometry 1920x1080 -depth 24
xrdp/Guacamole sessions read ~/.xsession; reconnect Guacamole to pick up changes.
13. Suggested paths and service names#
/opt/desk-portal/app.py # portal
/opt/guacamole/ # Guacamole stack
/home/desk/.vnc/ # VNC config
/home/desk/.xsession # xrdp session
/home/desk/Desktop/Inbox # uploads
/root/secrets/ # passwords and tokens (never commit)
Common systemd units:
desk-portal.servicedesk-websockify.servicedesk-audio.service(if you enable noVNC audio)cloudflared.servicexrdp/xrdp-sesman
14. Shortest path from zero#
- Buy a DO US 2C/4G Ubuntu 24.04 droplet
- SSH keys + UFW + user
desk - XFCE + TigerVNC (localhost)
- Install Claude Desktop and sign in on the desktop
- Portal + noVNC + upload
- Cloudflare Tunnel bound to a domain over HTTPS
- (Optional) Guacamole + xrdp + audio + UFW for Docker→guacd
- Disable sleep / autolock
- Fcitx5 Pinyin, Ctrl+Space
- Take a Snapshot; set renewal reminders
15. Disclaimer#
- Cloud-vendor TOS and Claude/Anthropic terms win; region and compliance are yours.
- This tutorial is system administration and remote desktop on a VPS you own. It does not teach bypassing anyone’s security or billing.
- Hostnames, IPs, and paths here come from one deployment. Replace them with yours.
Appendix A. Snapshot of this deployment (optional)#
| Item | Value |
|---|---|
| Hostname | claude-code-us |
| OS | Ubuntu 24.04 |
| Size | 2C / 4GB / NYC |
| Portal domain example | wi.ci |
| noVNC desktop | XFCE (stable for Claude) |
| Guacamole | Plasma + xrdp (audio) |
| IME | Fcitx5 Pinyin |
End of the public notes. Keep passwords, Tunnel IDs, and billing email in a private notebook.