---
title: "Running Claude Code safely on remote Linux"
description: "Install Claude Desktop on a US Ubuntu VPS and reach it through a Cloudflare Tunnel HTTPS portal — no public VNC or RDP."
canonical: https://yonnia.com/en/blog/linux-claude-code-secure-desktop
language: en
date: 2026-09-13
updated: 2026-09-13
tags: ["Linux","Claude","Security","Remote desktop"]
reading_minutes: 8
words: 1743
---
# 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

1. Sign in to DigitalOcean.
2. Create → Droplets → pick region, Ubuntu 24.04, 2C/4G.
3. Prefer **SSH Key** auth. If you must start with a password, switch to keys immediately and disable password login.
4. Hostname example: `claude-code-us`.
5. 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

```bash
ssh root@YOUR_DROPLET_IP
```

Do this next:

```bash
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:

```bash
## 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:

```bash
systemctl reload ssh
```

UFW baseline (allow SSH first, then enable):

```bash
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):

```bash
apt install -y xfce4 xfce4-goodies
## optional:
apt install -y kde-plasma-desktop
```

### 3.2 TigerVNC (loopback only)

```bash
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_ADDRESS` then `dbus-launch`
- Export IME environment (see section 8)
- `exec startxfce4` (or Plasma’s `startplasma-x11`)

Start (display `:1` → port `5901`):

```bash
sudo -u desk vncserver :1 -geometry 1920x1080 -depth 24
```

**Security**: VNC listens on `localhost` only. The public path is Tunnel / SSH forward.

```bash
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`: mask `sleep.target suspend.target hibernate.target`
- Plasma: disable Autolock in `~/.config/kscreenlockerrc`
- Put `xset s off -dpms` in the session startup script

---

## 4. Install Claude Desktop (Linux)

1. Install Claude Desktop the way Anthropic currently documents for Linux (apt package or the current official method).
2. Launch it as `desk` on the graphical desktop and complete login.
3. A desktop shortcut `start-claude-desktop.sh` that forces `DISPLAY=:1` helps.

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 `desk` password means fewer keyring interruptions.

Claude Code CLI (optional, complementary to Desktop):

```bash
## 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 `5901` into 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

1. Point a domain (example `wi.ci`) at Cloudflare DNS.
2. Install `cloudflared`, create a Tunnel, Ingress:

```yaml
ingress:
  - hostname: wi.ci
    service: http://127.0.0.1:8080
  - service: http_status:404
```

3. Add a portal password of its own (better if it differs from the VNC password).
4. 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

1. Install `xrdp` / `xorgxrdp`; `desk` must be allowed to log in.
2. Deploy Guacamole + guacd + PostgreSQL with Docker Compose.
3. nginx reverse-proxies `/guacamole/` and upgrades WebSocket.
4. **UFW must allow the Docker bridge to reach host guacd:4822** (otherwise you sit on Waiting / Bad gateway):

```bash
ufw allow from 172.16.0.0/12 to any port 4822
```

5. RDP audio: build/install `pulseaudio-module-xrdp` or the PipeWire xrdp module; in the session `PULSE_SINK=xrdp-sink`.
6. Example Guacamole connection: `127.0.0.1:3389`, user `desk`, `enable-audio=true`, `server-layout=en-us-qwerty`.

### 6.3 How the portal connects

- Dropdown: **Guacamole+audio** / **noVNC**
- The Guacamole iframe needs `tabindex` plus 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

```bash
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`, VNC `xstartup`, `~/.profile`):

```bash
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+Space` back to English, or reconnect Guacamole.

---

## 9. Secure-operation checklist (treat as mandatory)

1. **Do not open** 5901 / 3389 / 8080 / 4822 on the public internet. Only 22 (or a moved SSH port) plus Cloudflare.
2. SSH **key login**, password login off; root has no password login.
3. Portal password is not a throwaway; better if it differs from VNC.
4. 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`
5. Regular `apt upgrade`; watch Claude Desktop and kernel updates.
6. Snapshot: take a DigitalOcean Snapshot before large changes.
7. Do not post cookies, API keys, or desktop recordings in public places.
8. Memory alert: `free -h`. On OOM, close browser tabs / restart the desktop session first.

---

## 10. Daily use

1. Open `https://your-domain` → enter the portal password.
2. Pick a connection:
   - **noVNC**: daily desktop, Claude Desktop (XFCE)
   - **Guacamole+audio**: when you need system sound
3. Click the desktop area so keyboard focus is remote.
4. Chinese: `Ctrl+Space`.
5. Files: portal “Files” → drop into Inbox.
6. 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`:

```bash
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.service`
- `desk-websockify.service`
- `desk-audio.service` (if you enable noVNC audio)
- `cloudflared.service`
- `xrdp` / `xrdp-sesman`

---

## 14. Shortest path from zero

1. Buy a DO US 2C/4G Ubuntu 24.04 droplet
2. SSH keys + UFW + user `desk`
3. XFCE + TigerVNC (localhost)
4. Install Claude Desktop and sign in on the desktop
5. Portal + noVNC + upload
6. Cloudflare Tunnel bound to a domain over HTTPS
7. (Optional) Guacamole + xrdp + audio + UFW for Docker→guacd
8. Disable sleep / autolock
9. Fcitx5 Pinyin, Ctrl+Space
10. 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.*
