# code-server Administrator & Developer Guide

## 1. Architecture Overview

code-server wraps Visual Studio Code in a Node.js server process, exposing it as a browser-accessible web application with its own authentication, proxying, and CLI layer. It is built by Coder (coder.com), licensed MIT, and targets administrators who need to provide remote development environments accessible from any device with a modern browser.

### 1.1 High-Level Architecture

```
Browser (any device)
  │
  │  HTTPS / WSS
  ▼
┌─────────────────────────────────────────────────┐
│  code-server (Node.js 22 / Express 5)           │
│                                                  │
│  ┌──────────┐  ┌──────────┐  ┌───────────────┐ │
│  │ CLI/Entry │→│ Wrapper   │→│ Child Process  │ │
│  │ (entry.ts)│  │(parent)  │  │ (actual server)│ │
│  └──────────┘  └──────────┘  └───────┬───────┘ │
│                                       │         │
│  ┌────────────────────────────────────┤         │
│  │         Express Router             │         │
│  │  ┌─────┐ ┌─────┐ ┌──────┐ ┌────┐ │         │
│  │  │Login│ │Proxy│ │Health│ │VS  │ │         │
│  │  │Route│ │Route│ │Route │ │Code│ │         │
│  │  └─────┘ └─────┘ └──────┘ └────┘ │         │
│  └────────────────────────────────────┘         │
│                                                  │
│  VS Code Server (lib/vscode submodule + patches)│
└─────────────────────────────────────────────────┘
       │
       ▼
  Filesystem (extensions, settings, heartbeat)
```

### 1.2 Process Model — The Wrapper Pattern

code-server uses a parent/child process architecture implemented in `src/node/wrapper.ts`:

1. **Parent process** (`ParentProcess`): Spawns a child with the `--child` flag and an IPC channel. Forwards CLI arguments via a handshake message. Monitors the child for crashes and handles OS signals (SIGTERM, SIGINT, SIGHUP).
2. **Child process** (`ChildProcess`): Receives arguments from the parent over IPC, then starts the actual HTTP server. Calls `preventExit()` to override `process.exit()` — this is critical because VS Code's internals call `process.exit()` in several places, which would crash the entire server without this guard.
3. **ProcessError**: Wraps exit codes so the wrapper can distinguish intentional shutdowns from crashes.

This isolation layer exists specifically because VS Code was designed as a desktop application and calls `process.exit()` in code paths that a long-running server must survive.

### 1.3 Request Flow

```
Incoming request
  │
  ├─ Cookie parser middleware
  ├─ Common middleware (injects args, heart, settings, updater)
  ├─ TLS redirect (if --cert configured)
  │
  ├─ /_static/*           → Static file serving (no auth)
  ├─ Domain proxy check   → Domain-based proxy (if --proxy-domain matches hostname)
  ├─ /proxy/:port/*       → Path-based proxy with URL rewriting
  ├─ /absproxy/:port/*    → Absolute path proxy (passthrough, no rewriting)
  ├─ /healthz             → Health check (HTTP GET or WebSocket)
  ├─ /login               → Login page (GET) / password submission (POST)
  ├─ /logout              → Session clearing
  ├─ /update/check        → Version update check
  ├─ /vscode/*            → VS Code web client (legacy prefix)
  ├─ /*                   → VS Code web client (primary)
  └─ 404                  → Catch-all error handler
```

### 1.4 Component Inventory

| Component | Path | Role |
|-----------|------|------|
| Entry point | `src/node/entry.ts` | CLI parsing, dispatch to wrapper or VS Code CLI |
| CLI parser | `src/node/cli.ts` | Argument parsing, config file loading, defaults |
| App factory | `src/node/app.ts` | Express + HTTP(S) server creation |
| Route registry | `src/node/routes/index.ts` | Middleware chain and route mounting |
| VS Code route | `src/node/routes/vscode.ts` | Loads VS Code server module, delegates |
| Login route | `src/node/routes/login.ts` | Login page rendering, password validation, rate limiting |
| Logout route | `src/node/routes/logout.ts` | Cookie clearing |
| Health route | `src/node/routes/health.ts` | Health check (HTTP + WebSocket) |
| Update route | `src/node/routes/update.ts` | Version check endpoint |
| Domain proxy | `src/node/routes/domainProxy.ts` | Hostname-based port proxying |
| Path proxy | `src/node/routes/pathProxy.ts` | URL path-based port proxying |
| Error handlers | `src/node/routes/errors.ts` | HTTP + WebSocket error handling |
| HTTP utilities | `src/node/http.ts` | Auth, origin check, redirect, cookies, templates |
| Proxy server | `src/node/proxy.ts` | http-proxy instance with redirect rewriting |
| Heart | `src/node/heart.ts` | Heartbeat file writing, activity tracking |
| Settings | `src/node/settings.ts` | JSON file-based settings read/write |
| Socket proxy | `src/node/socket.ts` | TLS socket proxying for child processes |
| Update provider | `src/node/update.ts` | GitHub release checking, version comparison |
| Utilities | `src/node/util.ts` | Paths, password hashing (argon2/SHA256), cert generation |
| Constants | `src/node/constants.ts` | Version strings, XDG paths, proxy env |
| Wrapper | `src/node/wrapper.ts` | Parent/child process management |
| VS Code socket | `src/node/vscodeSocket.ts` | Editor session manager IPC |
| WS router | `src/node/wsRouter.ts` | WebSocket routing on top of Express |
| i18n | `src/node/i18n/index.ts` | i18next initialization, custom string loading |
| Emitter | `src/common/emitter.ts` | Event emitter with async support |
| HTTP types | `src/common/http.ts` | HttpCode enum, HttpError class, cookie session name |
| Common utilities | `src/common/util.ts` | plural, generateUuid, normalize, logError |
| Service worker | `src/browser/serviceWorker.ts` | Minimal PWA service worker |

### 1.5 Technology Stack

| Layer | Technology | Version |
|-------|-----------|---------|
| Runtime | Node.js | 22 |
| Language | TypeScript | 5.6 |
| HTTP framework | Express | 5.0.1 |
| WebSocket | ws | 8.14 |
| Password hashing | argon2 | 0.44 |
| Proxy | http-proxy | 1.18 |
| TLS | httpolyglot | 0.1.2 |
| Config parsing | js-yaml | 4.1 |
| i18n | i18next | 25.8 |
| Rate limiting | limiter | 2.1 |
| Proxy agent | proxy-agent | 6.3 |
| Version comparison | semver | 7.5 |
| Log rotation | rotating-file-stream | 3.1 |
| Path resolution | env-paths 2.2, xdg-basedir 4.0 | — |
| Process init (Docker) | dumb-init | — |
| VS Code | Submodule at `lib/vscode` | Patched via quilt |

### 1.6 VS Code Integration

VS Code source lives as a git submodule at `lib/vscode`. code-server applies 23+ quilt patches on top (see Section 8.5) to adapt the desktop editor for web serving. The VS Code server module is loaded dynamically by the VS Code route handler (`src/node/routes/vscode.ts`), which delegates all editor-related requests to VS Code's own HTTP handling.

---

## 2. System Requirements

### 2.1 Hardware

| Resource | Minimum | Recommended |
|----------|---------|-------------|
| CPU | 1 core | 2+ cores |
| RAM | 1 GB | 2+ GB (scales with extensions and open files) |
| Disk | 1 GB (base install) | 5+ GB (extensions, workspaces, user data) |

> [!NOTE]
> Memory consumption scales linearly with the number of open files, active extensions, and language server instances. A TypeScript project with several extensions can consume 2-4 GB.

### 2.2 Software

- **Node.js 22** (required, not optional)
- **Linux** is the primary target (Debian, Ubuntu, Fedora, openSUSE have Dockerfiles)
- **macOS** and **Windows** are supported but Linux is recommended for production
- A modern browser (Chrome, Firefox, Edge, Safari) on the client side

### 2.3 Network

- A single TCP port (default 8080) for HTTP/HTTPS and WebSocket traffic
- Optionally a Unix socket instead of TCP
- Outbound access to `api.github.com` for update checks (can be disabled)

---

## 3. Configuration Reference

### 3.1 Configuration Precedence

Configuration values are resolved in this order (highest wins):

1. **CLI flags** (e.g., `--auth none`)
2. **Environment variables** (e.g., `PASSWORD=secret`)
3. **Config file** (`config.yaml`)
4. **Built-in defaults**

### 3.2 Config File

The default config file location is `{config_dir}/config.yaml`. On first launch, code-server generates a default config with a random password:

```yaml
bind-addr: 127.0.0.1:8080
auth: password
password: <randomly-generated>
cert: false
```

Override the config file path with `--config <path>` or `$CODE_SERVER_CONFIG`.

### 3.3 Complete CLI Reference

| Flag | Short | Type | Default | Description |
|------|-------|------|---------|-------------|
| `--auth` | | `password` or `none` | `password` | Authentication type |
| `--password` | | string | (auto-generated) | Password for login. Set in config or `$PASSWORD` only, not command line. |
| `--hashed-password` | | string | | Argon2 hash of password. Takes precedence over `--password`. Set in config or `$HASHED_PASSWORD` only. |
| `--cert` | | optional string | `false` | Path to TLS certificate. If flag is present without a value, generates a self-signed cert. |
| `--cert-host` | | string | `localhost` | Hostname for self-signed certificate generation |
| `--cert-key` | | string | | Path to TLS private key. Required when `--cert` has a path value. |
| `--disable-telemetry` | | boolean | `false` | Disable telemetry |
| `--disable-update-check` | | boolean | `false` | Disable periodic update checks against GitHub releases |
| `--session-socket` | | string | `{data}/code-server-ipc.sock` | Path for the editor session IPC socket |
| `--cookie-suffix` | | string | | Suffix appended to cookie names. Also `$CODE_SERVER_COOKIE_SUFFIX`. |
| `--disable-file-downloads` | | boolean | `false` | Block file downloads. Also `$CS_DISABLE_FILE_DOWNLOADS`. |
| `--disable-file-uploads` | | boolean | `false` | Block file uploads |
| `--disable-workspace-trust` | | boolean | `false` | Disable the workspace trust prompt (session only) |
| `--disable-getting-started-override` | | boolean | `false` | Disable the Getting Started page override. Also `$CS_DISABLE_GETTING_STARTED_OVERRIDE`. |
| `--disable-proxy` | | boolean | `false` | Disable the built-in proxy routes. Also `$CS_DISABLE_PROXY`. |
| `--help` | `-h` | boolean | | Print help and exit |
| `--link-protection-trusted-domains` | | string[] | | Domains that skip link protection warnings |
| `--locale` | | string | | IETF language tag for UI localization |
| `--open` | | boolean | `false` | Open browser automatically (only works when bound to localhost) |
| `--bind-addr` | | string | `127.0.0.1:8080` | Address and port to bind. `$PORT` and `$CODE_SERVER_HOST` override the port and host components respectively. |
| `--config` | | string | `{config}/config.yaml` | Path to YAML config file. `$CODE_SERVER_CONFIG`. |
| `--host` | | string | | **Deprecated** — use `--bind-addr` |
| `--port` | | number | | **Deprecated** — use `--bind-addr` |
| `--socket` | | string | | Listen on a Unix socket instead of TCP. Overrides `--bind-addr`. |
| `--socket-mode` | | string | | File permissions for the Unix socket |
| `--trusted-origins` | | string[] | | Origins to allow. Supports `*` (all) and `*.domain.com` (wildcard subdomain). |
| `--version` | `-v` | boolean | | Print version and exit |
| `--user-data-dir` | | string | `{data}` | VS Code user data directory |
| `--extensions-dir` | | string | `{data}/extensions` | Extensions installation directory |
| `--builtin-extensions-dir` | | string | | Additional built-in extensions directory |
| `--list-extensions` | | boolean | | List installed extensions and exit |
| `--force` | | boolean | | Skip prompts during extension install |
| `--locate-extension` | | string[] | | Print the path of specified extensions |
| `--category` | | string | | Filter extensions by category |
| `--install-extension` | | string[] | | Install extensions. Format: `publisher.name[@version]` or path to `.vsix` |
| `--enable-proposed-api` | | string[] | | Enable proposed API for specified extensions |
| `--uninstall-extension` | | string[] | | Uninstall specified extensions |
| `--show-versions` | | boolean | | Show extension versions when listing |
| `--github-auth` | | string | | GitHub token for extension marketplace auth. Set in config or `$GITHUB_TOKEN` only. |
| `--proxy-domain` | | string[] | | Domain pattern for domain-based proxying. Format: `prefix-{{port}}.domain.tld` or `{{port}}.domain.tld`. |
| `--skip-auth-preflight` | | boolean | `false` | Allow OPTIONS requests without authentication (for CORS preflight) |
| `--ignore-last-opened` | `-e` | boolean | `false` | Do not reopen the last opened workspace |
| `--new-window` | `-n` | boolean | `false` | Open a new window |
| `--reuse-window` | `-r` | boolean | `false` | Reuse the existing window |
| `--log` | | enum | | Log level: `trace`, `debug`, `info`, `warn`, `error`. Also `$LOG_LEVEL`. |
| `--verbose` | `-vvv` | boolean | `false` | Sets log level to `trace` |
| `--app-name` | `-an` | string | `code-server` | Application name. Replaces the `{{app}}` placeholder in templates. |
| `--welcome-text` | `-w` | string | | **Deprecated** — Login page welcome text |
| `--abs-proxy-base-path` | | string | | Prefix prepended to absproxy requests |
| `--i18n` | | string | | Path to custom i18n JSON strings |
| `--idle-timeout-seconds` | | number | | Auto-shutdown after idle period. Must be >60. Also `$CODE_SERVER_IDLE_TIMEOUT_SECONDS`. |
| `--reconnection-grace-time` | | string | `10800` (3 hours) | Seconds to keep sessions alive for reconnection. Also `$CODE_SERVER_RECONNECTION_GRACE_TIME`. |

> [!IMPORTANT]
> The `--password`, `--hashed-password`, and `--github-auth` flags cannot be passed on the command line for security reasons. They must be set via config file or environment variables. The corresponding environment variables (`PASSWORD`, `HASHED_PASSWORD`, `GITHUB_TOKEN`) are cleared from the process environment immediately after being read.

### 3.4 Complete Environment Variable Reference

| Variable | Purpose |
|----------|---------|
| `PASSWORD` | Login password (cleared after read) |
| `HASHED_PASSWORD` | Argon2 hashed password (cleared after read; takes precedence over `PASSWORD`) |
| `GITHUB_TOKEN` | GitHub token for extension marketplace auth (cleared after read) |
| `PORT` | Override the port component of `--bind-addr` |
| `CODE_SERVER_HOST` | Override the host component of `--bind-addr` |
| `CODE_SERVER_CONFIG` | Path to config file (overrides default) |
| `CODE_SERVER_SESSION_SOCKET` | Path to session IPC socket |
| `CODE_SERVER_COOKIE_SUFFIX` | Suffix for cookie names |
| `CODE_SERVER_RECONNECTION_GRACE_TIME` | Reconnection grace period in seconds |
| `CODE_SERVER_IDLE_TIMEOUT_SECONDS` | Idle shutdown timeout in seconds |
| `LOG_LEVEL` | Log level (`trace`, `debug`, `info`, `warn`, `error`) |
| `CS_DISABLE_FILE_DOWNLOADS` | Set to `true` or `1` to disable file downloads |
| `CS_DISABLE_GETTING_STARTED_OVERRIDE` | Set to `true` or `1` to disable Getting Started override |
| `CS_DISABLE_PROXY` | Set to `true` or `1` to disable proxy routes |
| `EXTENSIONS_GALLERY` | JSON string for custom extensions marketplace configuration |
| `VSCODE_PROXY_URI` | URI pattern for VS Code's Ports tab |
| `HTTP_PROXY` / `http_proxy` | HTTP proxy for outbound connections |
| `HTTPS_PROXY` / `https_proxy` | HTTPS proxy for outbound connections |
| `NODE_ENV` | Set to `development` to enable i18n debug mode |
| `DOCKER_USER` | Docker: rename the default `coder` user |
| `ENTRYPOINTD` | Docker: directory of startup scripts to execute |
| `CODE_SERVER_PARENT_PID` | Internal: parent process PID for VS Code CLI |
| `VSCODE_IPC_HOOK_CLI` | Internal: VS Code CLI IPC socket path |

### 3.5 Data Directory Layout

| Platform | Data Directory | Config Directory | Runtime Directory |
|----------|---------------|------------------|-------------------|
| Linux | `~/.local/share/code-server` | `~/.config/code-server` | `$XDG_RUNTIME_DIR/code-server` or `/tmp` |
| macOS | `~/.local/share/code-server` | `~/.config/code-server` | `$XDG_RUNTIME_DIR/code-server` or `/tmp` |
| Windows | `%APPDATA%/code-server` | `%APPDATA%/code-server` | System temp directory |

> [!NOTE]
> macOS uses XDG-style paths, not native macOS `~/Library` paths. This is intentional.

Key files within the data directory:

| File/Directory | Purpose |
|----------------|---------|
| `config.yaml` | Main configuration (in config dir, auto-generated on first run) |
| `coder.json` | Settings file (last query, update check cache) |
| `heartbeat` | Heartbeat timestamp file (written every 60 seconds) |
| `code-server-ipc.sock` | Session manager IPC socket |
| `extensions/` | Installed VS Code extensions |
| `serve-web-key-half` | Half of the encryption key for VS Code web |

---

## 4. Deployment & Scaling

### 4.1 Standalone Installation

Download the latest release from [GitHub Releases](https://github.com/coder/code-server/releases) and run:

```bash
# Install script (Linux/macOS)
curl -fsSL https://code-server.dev/install.sh | sh

# Or manually extract and run
tar -xzf code-server-4.116.0-linux-amd64.tar.gz
./code-server-4.116.0-linux-amd64/bin/code-server
```

On first run, a config file is generated at `~/.config/code-server/config.yaml` with a random password. The server listens on `127.0.0.1:8080` by default.

### 4.2 Docker Deployment

The official Docker image is `codercom/code-server:4.116.0`. Base images are available for Debian (default), Fedora, and openSUSE.

```bash
docker run -d \
  --name code-server \
  -p 8080:8080 \
  -v "$HOME/.local:/home/coder/.local" \
  -v "$HOME/.config:/home/coder/.config" \
  -v "$PWD:/home/coder/project" \
  -u "$(id -u):$(id -g)" \
  -e "DOCKER_USER=$USER" \
  codercom/code-server:4.116.0
```

**Docker-specific behavior:**

- **Default user**: `coder` with UID 1000
- **`DOCKER_USER`**: Renames the `coder` user to match your host user (for cleaner `ps` output and shell prompts)
- **`ENTRYPOINTD`**: Directory of shell scripts executed during container startup (useful for installing system packages, pre-installing extensions, etc.)
- **Process supervisor**: Uses `dumb-init` as PID 1 to handle signal forwarding and zombie reaping. An alternative `catatonit` is also available.
- **fixuid**: Handles UID/GID remapping when the container user doesn't match the mounted volume ownership

> [!TIP]
> For persistent data across container recreations, mount `~/.local/share/code-server` (extensions, settings) and `~/.config/code-server` (config file). Mount your project directory separately.

**Docker Compose example:**

```yaml
services:
  code-server:
    image: codercom/code-server:4.116.0
    ports:
      - "8080:8080"
    volumes:
      - code-server-data:/home/coder/.local
      - code-server-config:/home/coder/.config
      - ./project:/home/coder/project
    environment:
      - DOCKER_USER=developer
      - PASSWORD=changeme
    restart: unless-stopped

volumes:
  code-server-data:
  code-server-config:
```

### 4.3 Kubernetes via Helm

The official Helm chart is at version 3.35.0 (appVersion 4.116.0).

```bash
helm repo add code-server https://helm.coder.com/code-server
helm install code-server code-server/code-server
```

**Helm chart defaults:**

| Setting | Default |
|---------|---------|
| Replicas | 1 |
| Service type | ClusterIP |
| Service port | 8080 |
| PVC size | 10Gi (ReadWriteOnce) |
| Security context | UID 1000 / GID 1000 |
| Liveness probe | Enabled |
| Readiness probe | Enabled |
| Volume permissions init container | Enabled |

**Key `values.yaml` options:**

```yaml
# Extra CLI arguments
extraArgs:
  - --auth=none
  - --disable-telemetry

# Extra environment variables
extraVars:
  - name: EXTENSIONS_GALLERY
    value: '{"serviceUrl": "https://marketplace.example.com/_apis/public/gallery"}'

# Password from existing Kubernetes secret
existingSecret:
  name: code-server-secret
  key: password

# Pre-install extensions via init container
extraInitContainers:
  - name: install-extensions
    image: codercom/code-server:4.116.0
    command: ["sh", "-c", "code-server --install-extension ms-python.python"]
    volumeMounts:
      - name: data
        mountPath: /home/coder

# Sidecar container (e.g., Docker-in-Docker)
extraContainers:
  - name: docker-dind
    image: docker:dind
    securityContext:
      privileged: true

# Ingress
ingress:
  enabled: true
  hosts:
    - host: code.example.com
      paths:
        - /

# Additional volume mounts
extraVolumeMounts: []
extraSecretMounts: []

# Lifecycle hooks
lifecycle: {}
```

> [!WARNING]
> code-server is a single-user application. Each instance serves one user. Do not set replicas > 1 for a single user. For multi-user deployments, create one code-server instance per user (separate Helm releases or a platform like Coder).

### 4.4 Reverse Proxy Configuration

code-server requires WebSocket support from any reverse proxy. The following are minimal configurations for common proxies.

**Nginx:**

```nginx
server {
    listen 443 ssl;
    server_name code.example.com;

    ssl_certificate     /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection upgrade;
        proxy_set_header Accept-Encoding gzip;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
```

**Traefik (Docker labels):**

```yaml
labels:
  - "traefik.enable=true"
  - "traefik.http.routers.code-server.rule=Host(`code.example.com`)"
  - "traefik.http.routers.code-server.tls.certresolver=letsencrypt"
  - "traefik.http.services.code-server.loadbalancer.server.port=8080"
```

**Caddy:**

```
code.example.com {
    reverse_proxy 127.0.0.1:8080
}
```

> [!IMPORTANT]
> When running behind a reverse proxy, set `--bind-addr 0.0.0.0:8080` (or the appropriate internal interface) and configure `--trusted-origins` to include your domain. If you terminate TLS at the proxy, do not use `--cert` on code-server itself.

### 4.5 systemd Service

```ini
[Unit]
Description=code-server
After=network.target

[Service]
Type=simple
User=coder
Group=coder
Environment=PASSWORD=changeme
ExecStart=/usr/bin/code-server --bind-addr 0.0.0.0:8080
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
```

### 4.6 Unix Socket Deployment

For reverse proxy setups where TCP isn't needed:

```bash
code-server --socket /run/code-server/code-server.sock --socket-mode 660
```

The reverse proxy connects to the Unix socket instead of a TCP port. This avoids port conflicts and can provide marginally better performance.

---

## 5. Operations

### 5.1 Health Monitoring

**HTTP health check:**

```bash
curl http://localhost:8080/healthz
# Response: {"status": "alive", "lastHeartbeat": <unix_timestamp>}
```

**WebSocket health check:** Connect a WebSocket to `/healthz` and send any message. The server responds with the same health JSON.

The heartbeat system writes a timestamp to `{data}/heartbeat` every 60 seconds. The `/healthz` endpoint returns the last heartbeat time, allowing external monitors to detect stalled processes (a running server that has stopped processing requests).

### 5.2 Update Management

By default, code-server checks GitHub releases for new versions periodically. The `/update/check` endpoint returns:

```json
{
  "checked": 1735689600,
  "latest": "4.116.0",
  "current": "4.115.0",
  "isLatest": false
}
```

Disable this with `--disable-update-check` or `CS_DISABLE_UPDATE_CHECK=true`.

### 5.3 Extension Management

Extensions are managed via the CLI:

```bash
# Install an extension
code-server --install-extension ms-python.python

# Install a specific version
code-server --install-extension ms-python.python@2024.1.0

# Install from a .vsix file
code-server --install-extension ./my-extension.vsix

# List installed extensions
code-server --list-extensions --show-versions

# Uninstall
code-server --uninstall-extension ms-python.python

# Skip confirmation prompts
code-server --force --install-extension ms-python.python
```

**Custom extension marketplace:** By default, code-server uses the Open VSX Registry. To use a custom marketplace:

```bash
export EXTENSIONS_GALLERY='{"serviceUrl": "https://marketplace.example.com/_apis/public/gallery", "itemUrl": "https://marketplace.example.com/items"}'
```

> [!CAUTION]
> The Microsoft Visual Studio Marketplace license prohibits use by non-Microsoft products. code-server uses Open VSX by default for this reason. Some extensions (GitHub Copilot, Microsoft-published extensions) may not be available on Open VSX. See known issue #7494 (Copilot Chat) and #7758 (Homebrew missing vscode.git extension).

### 5.4 Idle Timeout

Configure automatic shutdown after a period of inactivity:

```bash
code-server --idle-timeout-seconds 3600  # Shut down after 1 hour idle
```

The minimum value is 60 seconds. The idle timer resets on any browser activity that triggers the heartbeat. This is useful for cloud deployments where compute cost should be minimized.

### 5.5 Multi-Instance / Session Management

When code-server is already running and you invoke it with a file or folder argument, it uses the `EditorSessionManager` (via the IPC socket at `{data}/code-server-ipc.sock`) to open the file in the existing instance rather than starting a new server.

```bash
# Opens in the already-running instance
code-server /path/to/file.txt
```

Relevant flags:
- `--new-window` / `-n`: Force a new window
- `--reuse-window` / `-r`: Reuse existing window
- `--ignore-last-opened` / `-e`: Don't reopen the last workspace

### 5.6 Logging

Control log verbosity:

```bash
code-server --log debug          # debug level
code-server --verbose            # trace level (most verbose)
LOG_LEVEL=warn code-server       # via environment
```

Levels: `trace`, `debug`, `info`, `warn`, `error`.

Log output goes to stdout/stderr. Use `rotating-file-stream` (a dependency) if log rotation is configured. In Docker, rely on Docker's logging drivers to manage log retention.

### 5.7 Localization

Specify a locale for UI strings:

```bash
code-server --locale ja
```

Custom strings can be provided via `--i18n /path/to/strings.json`. The i18n system uses i18next internally. Setting `NODE_ENV=development` enables i18n debug mode.

---

## 6. Performance

### 6.1 Resource Consumption

code-server's resource consumption is dominated by VS Code and its extensions, not by the code-server wrapper layer. Key factors:

- **Memory**: Base ~200-400 MB. Each open workspace with language servers adds 100-500 MB. TypeScript projects with IntelliSense are the heaviest.
- **CPU**: Mostly idle. Spikes during extension host operations (IntelliSense, linting, search).
- **Disk I/O**: Extension installations, file watching, and search operations.
- **Network**: WebSocket traffic between browser and server. Proportional to typing speed, terminal output, and extension activity.

### 6.2 Reconnection Grace Time

The `--reconnection-grace-time` flag (default: 10800 seconds = 3 hours) controls how long the server keeps a session alive after the browser disconnects. During this period, unsaved work and terminal sessions are preserved. Reduce this value in resource-constrained environments:

```bash
code-server --reconnection-grace-time 1800  # 30 minutes
```

### 6.3 Proxy Performance

The built-in proxy (`/proxy/:port/*` and `/absproxy/:port/*`) uses Node.js `http-proxy`. It rewrites redirect headers for path-based proxying. For high-throughput proxied services, consider exposing them directly rather than routing through code-server's proxy.

### 6.4 Tuning Tips

- **Disable unused features**: `--disable-telemetry`, `--disable-update-check` to reduce background network calls
- **Limit extensions**: Each extension consumes memory and potentially CPU; install only what's needed
- **File watching**: Large projects with many files can exhaust inotify watchers on Linux. Increase `fs.inotify.max_user_watches`:

```bash
echo "fs.inotify.max_user_watches=524288" | sudo tee /etc/sysctl.d/40-code-server.conf
sudo sysctl --system
```

---

## 7. Security

### 7.1 Authentication

code-server supports two authentication modes:

| Mode | Flag | Behavior |
|------|------|----------|
| `password` (default) | `--auth password` | Login page with password prompt |
| `none` | `--auth none` | No authentication — anyone with network access can use the editor |

> [!CAUTION]
> Setting `--auth none` gives full shell and file access to anyone who can reach the server. Never use this on a network-accessible instance without additional access control (VPN, SSH tunnel, reverse proxy with its own auth).

### 7.2 Password Storage

Passwords are stored and verified using one of three methods, determined by `getPasswordMethod()` based on the hash format:

| Method | Format | Security |
|--------|--------|----------|
| **Argon2** (recommended) | Hash starts with `$argon` | Memory-hard, resistant to GPU/ASIC attacks |
| **SHA256** (legacy) | 64-character hex string | Fast hash, not recommended for passwords |
| **Plain text** | Anything else | No hashing, comparison only |

Generate an argon2 hash:

```bash
# Using npx
npx argon2-cli -e "your-password"

# Set in config
hashed-password: "$argon2i$v=19$m=4096,t=3,p=1$..."
```

> [!WARNING]
> Known issue #7696: If an attacker obtains the hashed password value, they can potentially use it directly to authenticate without knowing the plaintext. Store your config file with restrictive permissions (`chmod 600 ~/.config/code-server/config.yaml`).

### 7.3 Login Rate Limiting

The login endpoint is protected by a `RateLimiter` class (using the `limiter` library) with two limits:

- **2 attempts per minute** (burst protection)
- **12 attempts per hour** (sustained brute-force protection)

These limits are hard-coded and not configurable.

### 7.4 Origin Validation and CSRF Protection

- **HTTP requests**: The server validates the `Origin` header against the `Host` header. Mismatches are rejected unless the origin is in `--trusted-origins`.
- **WebSocket connections**: Origin checking prevents cross-site WebSocket hijacking.
- **`--trusted-origins`**: Accepts exact domains, `*` (allow all), or `*.domain.com` (wildcard subdomain matching).
- **`--skip-auth-preflight`**: Allows `OPTIONS` requests without authentication, required for CORS preflight in some proxy setups.

### 7.5 Session Management

- Sessions use HTTP cookies with `sameSite=lax`
- Cookie domain is derived from the highest-level proxy domain (if configured)
- Cookie path is scoped to the base path
- `--cookie-suffix` allows disambiguating cookies when running multiple instances on the same domain

### 7.6 Sensitive Environment Variable Handling

The following environment variables contain secrets and are **cleared from `process.env` immediately after being read** by the CLI parser:

- `PASSWORD`
- `HASHED_PASSWORD`
- `GITHUB_TOKEN`

This prevents accidental leakage via child processes, `/proc/*/environ`, or crash dumps.

### 7.7 TLS Configuration

**Self-signed certificate (development/testing):**

```bash
code-server --cert
# Generates a self-signed cert for --cert-host (default: localhost)
```

**Bring your own certificate (production):**

```bash
code-server --cert /path/to/cert.pem --cert-key /path/to/key.pem
```

When `--cert` is configured:
- The server uses `httpolyglot` to serve both HTTP and HTTPS on the same port
- HTTP requests are automatically redirected to HTTPS
- The TLS redirect middleware is injected into the route chain

For production, terminate TLS at a reverse proxy (nginx, Traefik, Caddy) rather than using code-server's built-in TLS.

### 7.8 Proxy Security

The built-in proxy routes (`/proxy/:port/*`, `/absproxy/:port/*`, and domain-based proxy) require authentication. This means that services running on the same host are only accessible through code-server to authenticated users.

Disable the proxy entirely with `--disable-proxy` if it's not needed.

### 7.9 File Transfer Controls

```bash
code-server --disable-file-downloads  # Prevent downloading files
code-server --disable-file-uploads    # Prevent uploading files
```

These flags are useful in environments where data exfiltration or introduction of unauthorized files is a concern.

### 7.10 Security Hardening Checklist

1. Use `--auth password` with an argon2 hashed password
2. Restrict config file permissions: `chmod 600 ~/.config/code-server/config.yaml`
3. Bind to localhost (`127.0.0.1`) and use a reverse proxy with TLS for external access
4. Set `--trusted-origins` to your specific domain(s), never `*` in production
5. Use `--disable-proxy` if the proxy feature is not needed
6. Use `--disable-file-downloads` and `--disable-file-uploads` if file transfer is not required
7. Run as a non-root user (the Docker image defaults to UID 1000)
8. Keep code-server updated (or disable update checks and manage updates externally)
9. Increase `fs.inotify.max_user_watches` for Linux deployments to prevent DoS via file watching exhaustion

---

## 8. Developer Internals

### 8.1 Build System

The build process involves three main steps:

1. **Compile TypeScript**: `src/` is compiled to JavaScript
2. **Build VS Code**: The `lib/vscode` submodule is built with patches applied
3. **Package**: The result is packaged into distributable tarballs or Docker images

Quilt patches in `patches/` are applied to the VS Code submodule before building. These patches adapt the desktop VS Code for web serving (see Section 8.5).

### 8.2 Entry Point Dispatch Logic

`src/node/entry.ts` follows this decision tree:

```
entry.ts
  ├─ Is this a child process (--child flag)?
  │   └─ Yes → Handshake with parent via IPC, run server directly
  │
  ├─ Is --help or --version?
  │   └─ Yes → Print and exit
  │
  ├─ Is an extension flag present (--install-extension, etc.)?
  │   └─ Yes → runCodeCli() — spawns VS Code CLI subprocess
  │
  ├─ Is a file/folder argument with an existing instance?
  │   └─ Yes → openInExistingInstance() via IPC socket
  │
  └─ Otherwise → wrapper.start(args) — spawn child process
```

### 8.3 Server Creation (app.ts)

`createApp()` in `src/node/app.ts`:

1. Creates an Express router
2. If `--cert` is configured: creates an `httpolyglot` server (serves HTTP + HTTPS on the same port)
3. Otherwise: creates a plain `http.createServer`
4. Creates a separate `wsRouter` for WebSocket handling via the `handleUpgrade` mechanism
5. Creates an `EditorSessionManager` server on a separate Unix socket for IPC between instances
6. Returns `{ router, wsRouter, server, dispose() }`

### 8.4 Route Registration (routes/index.ts)

The route registration in `src/node/routes/index.ts` follows this exact order:

1. **Heartbeat system**: Starts writing to `{data}/heartbeat` every 60 seconds
2. **Cookie parser**: Parses cookies on all requests
3. **Common middleware**: Injects `args`, `heart`, `settings`, `updater`, `cookieSessionName` into the request object
4. **TLS redirect**: If `--cert` is configured, redirects HTTP to HTTPS
5. **Static files**: `/_static` serves static assets (no auth required)
6. **Domain proxy**: Checked first via hostname matching against `--proxy-domain` patterns
7. **Path proxy**: `/proxy/:port/*` (rewriting) and `/absproxy/:port/*` (passthrough)
8. **Health check**: `/healthz` (HTTP GET and WebSocket)
9. **Login/logout**: `/login` and `/logout` (only mounted when `--auth password`)
10. **Update check**: `/update/check`
11. **VS Code**: Mounted at both `/vscode` and `/` (dual prefix for legacy compatibility)
12. **404 catch-all**: Returns error page
13. **Error handlers**: Separate handlers for HTTP and WebSocket errors

### 8.5 VS Code Patches

The `patches/` directory contains 23+ quilt patches applied to the VS Code submodule. Each patch adapts a specific VS Code behavior for the browser-served context:

| Patch | Purpose |
|-------|---------|
| `base-path` | Support for serving behind a sub-path |
| `cli-window-open` | Adapt window opening for browser |
| `clipboard` | Browser clipboard integration |
| `copilot` | Copilot compatibility adjustments |
| `disable-builtin-ext-update` | Prevent built-in extensions from self-updating |
| `display-language` | Display language / locale support |
| `external-file-actions` | Handle external file operations in browser |
| `getting-started` | Custom Getting Started page |
| `insecure-notification` | Suppress insecure-connection warnings |
| `integration` | Core integration with code-server's HTTP layer |
| `keepalive` | WebSocket keepalive for long connections |
| `local-storage` | Browser localStorage adaptations |
| `logout` | Add logout functionality |
| `marketplace` | Extension marketplace (Open VSX) integration |
| `proposed-api` | Enable proposed API support |
| `proxy-uri` | Proxy URI handling for ports tab |
| `service-worker` | PWA service worker |
| `signature-verification` | Extension signature verification handling |
| `sourcemaps` | Source map handling for debugging |
| `store-socket` | Store socket path for IPC |
| `telemetry` | Telemetry handling |
| `trusted-domains` | Link protection and trusted domains |
| `unique-db` | Unique database paths per instance |
| `update-check` | Update notification integration |
| `webview` | Webview resource handling for browser |

### 8.6 Key Internal Modules

**`src/node/http.ts`** — HTTP utilities:
- `authenticated()`: Checks cookie session for valid authentication
- `ensureAuthenticated()`: Middleware that redirects to login if not authenticated
- `ensureOrigin()`: Validates Origin header against Host and trusted origins
- `redirect()`: Constructs redirect responses with proper base path handling
- `replaceTemplates()`: Replaces `{{CS_*}}`, `{{BASE}}`, `{{APP_NAME}}` placeholders in HTML

**`src/node/proxy.ts`** — Proxy server:
- Creates an `http-proxy` instance
- Rewrites `Location` headers in redirect responses to maintain path-based routing
- Handles both HTTP and WebSocket proxying

**`src/node/heart.ts`** — Heartbeat:
- `Heart` class writes the current timestamp to `{data}/heartbeat` every 60 seconds
- `beat()` is called on activity events
- `alive()` returns the last heartbeat timestamp

**`src/node/settings.ts`** — Settings:
- `SettingsProvider<T>` reads and writes JSON settings files
- Used for `coder.json` (update check cache, last query time)

**`src/node/update.ts`** — Update provider:
- `UpdateProvider` checks GitHub releases for newer versions
- Caches the result to avoid excessive API calls
- Compares versions using the `semver` library

**`src/common/emitter.ts`** — Emitter:
- `Emitter<T>` provides type-safe event emitting with `async` support
- Used throughout for internal event communication

### 8.7 WebSocket Router

`src/node/wsRouter.ts` provides WebSocket routing on top of Express. Since Express does not natively handle WebSocket upgrades, this module intercepts `upgrade` events on the HTTP server and routes them through a parallel set of route handlers that mirror the Express routing pattern.

### 8.8 Testing

| Test Type | Location | Framework |
|-----------|----------|-----------|
| Unit tests | `test/unit/` | Jest + ts-jest |
| Script tests | `test/scripts/` | bats (Bash Automated Testing System) |
| Integration tests | `test/integration/` | Jest |
| E2E tests | `test/e2e/` | Playwright |

- **Coverage threshold**: 60% line coverage minimum
- **Test isolation**: Tests have their own `package.json` (`test/package.json`) to avoid type conflicts with the VS Code submodule's TypeScript types
- **E2E auth state**: `globalSetup` preserves authentication state across Playwright tests

```bash
# Run unit tests
npm test

# Run e2e tests
npx playwright test

# Run script tests (requires bats)
bats test/scripts/
```

### 8.9 Project Structure

```
code-server/
├── src/
│   ├── node/              # Server-side TypeScript
│   │   ├── entry.ts       # Main entry point
│   │   ├── cli.ts         # CLI argument parser
│   │   ├── app.ts         # Express + HTTP server factory
│   │   ├── wrapper.ts     # Parent/child process management
│   │   ├── routes/        # Route handlers
│   │   │   ├── index.ts   # Route registration
│   │   │   ├── vscode.ts  # VS Code delegation
│   │   │   ├── login.ts   # Authentication
│   │   │   ├── health.ts  # Health check
│   │   │   ├── pathProxy.ts
│   │   │   ├── domainProxy.ts
│   │   │   └── errors.ts
│   │   ├── http.ts        # HTTP utilities
│   │   ├── proxy.ts       # Proxy server
│   │   ├── heart.ts       # Heartbeat
│   │   ├── settings.ts    # JSON settings
│   │   ├── update.ts      # Update checker
│   │   ├── util.ts        # Utilities
│   │   ├── constants.ts   # Version, paths
│   │   ├── socket.ts      # TLS socket proxy
│   │   ├── vscodeSocket.ts # Session IPC
│   │   ├── wsRouter.ts    # WebSocket router
│   │   └── i18n/          # Localization
│   ├── common/            # Shared code (server + browser)
│   │   ├── emitter.ts
│   │   ├── http.ts
│   │   └── util.ts
│   └── browser/           # Browser-side code
│       └── serviceWorker.ts
├── lib/
│   └── vscode/            # VS Code submodule (git submodule)
├── patches/               # Quilt patches for VS Code
├── test/
│   ├── unit/
│   ├── integration/
│   ├── e2e/
│   ├── scripts/
│   └── package.json       # Separate test dependencies
├── ci/                    # CI/CD configuration
└── docs/                  # Documentation
```

---

## 9. Integration Points

### 9.1 HTTP API Reference

All endpoints served by code-server:

| Method | Path | Auth Required | Response |
|--------|------|:---:|---------|
| GET | `/healthz` | No | `{"status": "alive", "lastHeartbeat": <timestamp>}` |
| WS | `/healthz` | No | Send any message, receive health JSON |
| GET | `/login` | No | HTML login page |
| POST | `/login` | No | Validates password, sets session cookie, redirects |
| GET | `/logout` | No | Clears session cookie, redirects to login |
| GET | `/update/check` | Yes | `{"checked": <ts>, "latest": "x.y.z", "current": "x.y.z", "isLatest": bool}` |
| GET | `/manifest.json` | No | PWA manifest |
| POST | `/mint-key` | No | VS Code web encryption key |
| GET | `/robots.txt` | No | Robots exclusion |
| GET | `/security.txt` | No | Security contact information |
| ALL | `/proxy/:port/*` | Yes | Path-based proxy with URL rewriting |
| ALL | `/absproxy/:port/*` | Yes | Absolute path proxy (no rewriting) |
| ALL | `/*` (domain proxy) | Yes | Domain-based proxy via `--proxy-domain` matching |
| GET | `/vscode/*` | Yes | VS Code web client (legacy prefix) |
| GET | `/*` | Yes | VS Code web client (primary) |
| GET | `/_static/*` | No | Static assets |

### 9.2 Proxy Modes

code-server provides three proxy modes for accessing services running on the same host:

**Path-based proxy (`/proxy/:port/*`):**
- Rewrites URLs in responses to maintain correct routing
- Example: `https://code.example.com/proxy/3000/` proxies to `localhost:3000`
- Rewrites `Location` headers in redirects

**Absolute path proxy (`/absproxy/:port/*`):**
- Passes requests through without URL rewriting
- Configured base path via `--abs-proxy-base-path`
- Useful when the proxied application handles its own path prefix

**Domain-based proxy (`--proxy-domain`):**
- Routes based on hostname matching
- Format: `prefix-{{port}}.domain.tld` or `{{port}}.domain.tld`
- Example: `--proxy-domain "{{port}}.proxy.example.com"` routes `3000.proxy.example.com` to `localhost:3000`
- Requires wildcard DNS and TLS certificate

The `VSCODE_PROXY_URI` environment variable integrates with VS Code's Ports tab, allowing forwarded ports to appear in the VS Code UI.

### 9.3 Extension Marketplace

By default, code-server connects to the Open VSX Registry. Override with the `EXTENSIONS_GALLERY` environment variable:

```bash
export EXTENSIONS_GALLERY='{
  "serviceUrl": "https://marketplace.example.com/_apis/public/gallery",
  "itemUrl": "https://marketplace.example.com/items"
}'
```

### 9.4 GitHub Integration

The `--github-auth` / `$GITHUB_TOKEN` setting provides a token that VS Code extensions can use for GitHub API authentication (e.g., for GitHub Pull Requests extension). The token is cleared from the environment after being read.

### 9.5 PWA Support

code-server includes a minimal service worker (`src/browser/serviceWorker.ts`) and PWA manifest (`/manifest.json`), allowing it to be installed as a Progressive Web App on supported browsers. This provides an app-like experience with its own window and taskbar icon.

### 9.6 IPC and Session Management

The `EditorSessionManager` (in `src/node/vscodeSocket.ts`) listens on a Unix socket (`{data}/code-server-ipc.sock`) and enables:

- Opening files in an existing instance from the command line
- Window management (`--new-window`, `--reuse-window`)
- Session discovery

---

## Appendix A: Troubleshooting

### A.1 Cannot find module 'argon2'

**Error:** `Cannot find module 'argon2'` on startup.

**Cause:** The argon2 native module failed to compile or is missing for the current platform/architecture.

**Solution:** Reinstall dependencies with `npm rebuild argon2` or use a SHA256 hashed password as a workaround. The Docker image includes pre-built argon2 binaries.

**Prevention:** Use the official release binaries or Docker image, which include pre-compiled native modules.

**Reference:** GitHub issue #7588 (closed/resolved).

### A.2 Copilot Chat Not Working

**Error:** GitHub Copilot Chat extension fails to activate or respond.

**Cause:** Copilot extensions may require specific VS Code APIs or authentication flows that differ between desktop VS Code and code-server's web environment.

**Solution:** Ensure you are using the latest code-server version. Check that the Copilot patch is applied correctly. Some Copilot features may have limited support in the web context.

**Reference:** GitHub issue #7494.

### A.3 Extension Not Found or Wrong Version Installed

**Error:** CLI installs an older extension version than expected.

**Cause:** The Open VSX Registry may not have the latest version of the extension, or the version resolution differs from the VS Code Marketplace.

**Solution:** Specify the exact version: `code-server --install-extension publisher.name@x.y.z`. Alternatively, download the `.vsix` file directly and install it: `code-server --install-extension ./extension.vsix`.

**Reference:** GitHub issue #7631.

### A.4 Always Asks to Trust Workspace

**Error:** The workspace trust dialog appears every time.

**Cause:** Workspace trust state may not persist correctly in certain configurations.

**Solution:** Use `--disable-workspace-trust` to suppress the dialog entirely.

**Reference:** GitHub issue #7703.

### A.5 Dynamic Webview Resources Fail

**Error:** Webview content (e.g., extension UI panels) fails to load in some browsers.

**Cause:** Browser security policies or proxy configurations interfere with dynamically loaded webview resources.

**Solution:** Ensure your reverse proxy passes all required headers and does not strip or modify content. Check browser console for CSP or network errors.

**Reference:** GitHub issue #3410.

### A.6 Random Text Insertion

**Error:** Random characters appear in the editor.

**Cause:** Input method or keyboard event handling differences between browser and native VS Code.

**Solution:** Update to the latest version. Try a different browser. Disable browser extensions that intercept keyboard events.

**Reference:** GitHub issue #7707.

### A.7 --app-name Does Not Set Page Title

**Error:** Setting `--app-name` changes the login page branding but not the browser tab title.

**Cause:** The page title is controlled by VS Code's internal logic, not the `--app-name` template replacement.

**Reference:** GitHub issue #7688.

### A.8 Security Vulnerabilities (Snyk)

**Error:** Snyk or other vulnerability scanners report issues.

**Cause:** Transitive dependencies may have known vulnerabilities.

**Solution:** Update to the latest code-server release, which periodically updates dependencies. Review the specific CVEs reported and assess whether they are exploitable in your deployment context.

**Reference:** GitHub issue #7737.

---

## Appendix B: What Does NOT Exist

This section explicitly documents what code-server does **not** have, to prevent assumptions:

- **No CLI subcommands**: code-server uses a flat flag-based CLI parser, not a subcommand structure
- **No built-in API beyond the routes listed in Section 9.1**: There is no REST API for managing users, workspaces, or settings programmatically
- **No database**: All state is file-based (config YAML, JSON settings, filesystem)
- **No background job queue**: Everything is synchronous request/response or WebSocket
- **No plugin system**: An earlier version (v1) had a plugin API; it was removed. Extensions are VS Code extensions, not code-server plugins.
- **No multi-user support**: Each instance serves exactly one user. Multi-user requires multiple instances (or a platform like Coder).
- **No built-in OAuth/SSO**: Authentication is password-only. Use a reverse proxy (oauth2-proxy, Authelia, etc.) for SSO.
- **No native macOS paths**: macOS uses XDG-style paths (`~/.local/share`, `~/.config`), not `~/Library`.

---

_Generated May 2026 from coder/code-server at 5f7c23b. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
