Trace workspace connections
Correlate client, coderd, and agent logs by session ID for SSH, IDE, and web terminal connections.
On this page
This guide is for Coder deployment administrators and platform teams who need to correlate workspace connection logs with client session IDs.
It covers coder ssh, VS Code, JetBrains Toolbox, and the web terminal.
You'll collect logs from the user's client, coderd (the process that runs the control plane), and the workspace agent.
Then you'll match entries by session ID and read the connection and workspace lifecycle fields.
For connection log filters and export formats, refer to Connection logs.
To diagnose a connection that is still open, refer to Networking troubleshooting.
Before you begin
- Update the Coder CLI and workspace agents to the same version as your control plane. Agents from earlier versions keep working, but they don't record client session IDs, app names, workspace shutdown reasons, or bytes transferred.
- Collect
coderdlogs with a log management tool such as Splunk, Datadog, or Grafana Loki. JSON output is best for querying by other programs, such asjq. To write JSON logs, setCODER_LOGGING_JSONto a file path, such as/dev/stderr. Human-readable logs also go to/dev/stderrby default, so setCODER_LOGGING_HUMANto/dev/nullto keep only JSON there. - The
connection_logentries in this guide require a Premium license. Client and agent logs don't. - For VS Code, use the Coder Remote extension version 1.17.2 or later.
- For JetBrains Toolbox, use the Coder Toolbox plugin version 0.9.5 or later to send session IDs and to include
coder sshlogs in Collect logs. Plugin version 0.9.5 requires JetBrains Toolbox version 3.9.0 or later.
How client session IDs work
Each supported client creates a client session ID, a 32-character lowercase hexadecimal value.
The client writes the ID to its own logs and sends it with its requests to coderd and the workspace agent.
coderd, workspace proxies, and the agent add the same ID to their log lines as client_session_id.
The agent also records it in the connection log as ClientSessionID for SSH sessions, web terminal connections, and JetBrains Toolbox forwarded-channel connections.
Search each log source for one value to follow a session from the client, through coderd, to the agent.
What counts as one session depends on the client:
| Client | One session covers |
|---|---|
coder ssh, including OpenSSH connections that use coder config-ssh | One command run. |
| VS Code | One VS Code window. Reloading the window starts a new session. |
| JetBrains Toolbox | One workspace agent that Toolbox connects to. |
| Web terminal | One page load. Reloading the page or opening a new tab starts a new one. |
VS Code and JetBrains Toolbox pass their session ID to the coder ssh process they start, so the IDE log, the CLI log, and the server-side logs share one ID.
To correlate your own tooling the same way, set CODER_TRACE_SESSION_ID to a 32-character lowercase hexadecimal value before you run coder.
coderd and the agent ignore values in any other format.
One client session often spans several SSH sessions or web terminal connections.
For example, an IDE that reconnects after a network interruption opens a new SSH connection each time, and one SSH connection can carry several SSH sessions.
Each SSH session or web terminal connection has its own ConnectionID, shared by its connected and disconnected connection log entries, and the same ClientSessionID as the rest of the client session.
Find the session ID
Ask the user for their client logs from the session, then find the client_session_id field.
If the user can't provide them, start from the connection log for their workspace and time window instead, as described in Correlate a session.
coder ssh
coder ssh writes one log file per session.
The default directory depends on the operating system:
| Operating system | Default directory |
|---|---|
| Linux | ~/.local/state/coder/logs, or $XDG_STATE_HOME/coder/logs if it's set |
| macOS | ~/Library/Application Support/coder/logs |
| Windows | %LOCALAPPDATA%\coder\logs |
File names start with coder-ssh- followed by the date and time the session started, for example coder-ssh-20261013-141502-abcde.log.
When coder ssh runs as an OpenSSH proxy command, the name also ends with the parent process ID.
Before it creates a new file, the CLI deletes all but the 50 most recently modified coder-*.log files in the directory.
If removal fails, the CLI logs a warning and continues the session.
To write them somewhere else, set --log-dir or CODER_SSH_LOG_DIR.
Every line in the file includes client_session_id.
By default, the file records info-level entries.
The CLI also keeps the 10 most recent debug entries in memory and writes them to the file when the command logs an error.
To keep more, set --flight-recorder-size or CODER_FLIGHT_RECORDER_SIZE.
To record every debug entry, set --verbose or CODER_VERBOSE=true.
VS Code
The extension starts coder ssh with debug logging and passes it the session ID.
You can get the session ID from VS Code in three ways:
- Support bundle: Ask the user to run Coder: Create Support Bundle from the Command Palette.
If the workspace agent is reachable, the bundle contains the agent log in
agent/logs.txt. Undervscode-logs/, it contains thecoder sshlogs, the extension logs, and the Remote - SSH logs. For more details, refer to Generate a support bundle. coder sshlog directory: The extension writes thecoder sshlogs to the directory in thecoder.proxyLogDirectorysetting. If the setting is empty, it usesCODER_SSH_LOG_DIR, and if neither is set, alogdirectory in the extension's global storage. For the file names and contents, refer tocoder ssh.- Coder output channel: Each line starts with
[client_session_id=<session-id>], with the full 32-character session ID. The extension keeps the 1,000 most recent entries below the channel's log level and writes them to the channel when a connection fails. To change the number of entries, setcoder.connectionLogBuffer.size. If the window reloaded or closed after the failure, the channel no longer shows the earlier output. To read the saved output, run Developer: Open Extension Logs Folder from the Command Palette, then openCoder.login thecoder.coder-remotefolder.
JetBrains Toolbox
Toolbox starts coder ssh.
Toolbox log lines for a session include the full 32-character client_session_id.
With Coder Toolbox plugin version 0.9.5 or later, the plugin first checks the SSH log directory in the Coder plugin settings.
If that setting is empty, it uses CODER_SSH_LOG_DIR.
If neither is set, it uses an ssh-logs directory in the plugin's data directory for the deployment.
Ask the user to select Collect logs from the workspace action menu.
With plugin version 0.9.5 or later, successful collection includes Toolbox logs and a nested Coder support bundle, coder-support.zip.
The nested bundle contains recent coder ssh logs under toolbox-logs/ssh/.
If Coder bundle collection fails, the plugin attempts to include the SSH logs in coder-ssh.zip and collection details in coder-support-error.txt.
With earlier plugin versions, Collect logs includes only the Toolbox logs.
These versions write coder ssh logs to the coder ssh log directory, unless an SSH log directory is set in the plugin settings.
Ask the user for those files separately and generate a support bundle with the CLI.
For details, refer to Generate a support bundle and JetBrains Toolbox troubleshooting.
Web terminal
The web terminal logs connection errors, including client_session_id, to the browser's developer console.
If the user didn't keep the console output, find the session in the agent logs or the connection log by workspace and time.
Collect server-side logs
Gather coderd and agent logs that cover the session's time window.
coderd logs
coderd writes a connection_log entry at the info level each time a connection opens or closes, so these entries don't require debug logging.
For SSH sessions and web terminal connections, the agent reports both events, so each one produces a connected entry and a disconnected entry with the same ConnectionID.
JetBrains Toolbox sessions work differently, as described in Agent logs.
The fields that matter most for tracing are in the following table:
| Field | Description |
|---|---|
ClientSessionID | The client session ID. Empty for workspace app, port forwarding, and tunnel events, and for connections from older clients or agents. |
ConnectionID | Identifies an SSH session, web terminal connection, or tracked JetBrains forwarded channel. For SSH sessions, matches the id field in the agent log. |
ConnectionStatus | connected or disconnected. |
ConnectionMethod | How the connection was made, such as ssh or reconnecting_pty. |
AppNameOrPort | The app that made an SSH or web terminal connection, such as vscode or cursor. |
DisconnectReason | Why the connection closed, on disconnected entries. Refer to Read disconnect reasons. |
RxBytes, TxBytes | Bytes the agent received and sent over an SSH or web terminal connection. Set only on disconnected entries, and empty for older agents. |
For every field and an example entry, refer to Service logs.
coderd and workspace proxies also add client_session_id to request logs for API requests and path-based workspace app requests.
Requests that fail with a 5xx status are logged at the warn level.
All other requests, including 4xx failures such as 401, 403, and 404, are logged at the debug level, so they appear only when debug logging is on.
To turn it on, set CODER_LOG_FILTER to .*, which greatly increases log volume.
Agent logs
The workspace agent writes its log to coder-agent.log in the directory set by CODER_AGENT_LOG_DIR.
The agent rotates the file at 5 MB and keeps up to 19 rotated files named coder-agent-<timestamp>.log in the same directory.
If CODER_AGENT_LOG_DIR isn't set, the agent uses the system temporary directory.
On Linux and macOS, that's $TMPDIR if it's set, or /tmp otherwise.
On Windows, it's the user's temporary directory, usually %TEMP%.
In Kubernetes workspaces, the same entries are also in the pod logs.
To collect the agent log without workspace access, run coder support bundle <owner>/<workspace> and open agent/logs.txt.
For details, refer to Generate a support bundle.
By default, the agent logs at the info level.
It keeps the 1,000 most recent debug entries in memory and writes them to the log when it loses its connection to coderd or before it logs an error.
To change the number of entries, set CODER_AGENT_FLIGHT_RECORDER_SIZE in the agent's environment.
A value of 0 turns it off.
To record every debug entry, set CODER_VERBOSE=true in the agent's environment.
Search the agent log for these messages:
| Message | What it records |
|---|---|
handling ssh session | An SSH session started. |
ssh session closed | An SSH session ended. Coder includes disconnect_reason, disconnect_expected, and exit_code. |
ssh session failed | An SSH session failed with an error. The error text becomes the disconnect_reason. |
ssh connection complete | An SSH connection closed. If the agent was shutting down, Coder also includes disconnect_reason, disconnect_expected, and disconnect_initiator. |
ssh connection failed | An SSH connection failed before a session started. |
accepted conn, reconnecting pty closed | A web terminal connection opened or closed, from the reconnecting-pty logger. The closing entry includes disconnect_reason. |
disconnected from coderd | The agent lost its connection to coderd. The buffered debug entries are written just before this line. |
The speedtest logger also writes accepted conn for coder speedtest connections.
Use the logger name to distinguish those entries from web terminal connections.
JetBrains Toolbox opens many short SSH sessions, so handling ssh session lines are frequent for Toolbox users.
For those sessions, the agent doesn't log ssh session closed or report them to the connection log.
Instead, it reports the forwarded channel that Toolbox keeps open as one connection, and logs JetBrains watcher accepted channel and JetBrains channel closed at the debug level.
Correlate a session
With a session ID and the logs, rebuild the timeline of the session.
Tip
To see every connection in the session from the dashboard, open the Connection Log page, open an entry's details, and select View related connections.
The page lists the connections in the same workspace that share the client session ID.
The details also show the Client session ID with a copy button, which you can use to search the coderd and agent logs.
-
Search the
coderdlogs forconnection_logentries where theClientSessionIDmatches the session ID.For example, with JSON logs in a file:
jq -c 'select(.msg == "connection_log" and .fields.ClientSessionID == "<session-id>")' coderd.logEach entry names the workspace, agent, connection method, app, and
ConnectionID. -
Note the time of each
connectedanddisconnectedentry and itsDisconnectReason. -
Search the
coderdlogs forclient_session_idset to the same value.Warn-level entries show
5xxresponses. Debug logs also include4xxresponses, as described incoderdlogs. -
Search the agent log for the session ID:
On Linux and macOS:
grep '<session-id>' <agent-log-dir>/coder-agent*.logOn Windows workspaces, use PowerShell:
Select-String -Path '<agent-log-dir>\coder-agent*.log' -Pattern '<session-id>' -SimpleMatchThe matching lines show each SSH or web terminal session the client opened and the reason it closed.
-
Line up the client log with the server-side entries by time.
Matching timestamps and session IDs connect the client activity to the reported connection events.
A client session ID doesn't guarantee a corresponding connection_log entry.
Connections that don't reach the agent and earlier agents that don't record session IDs can leave no matching entries.
Reports can also be lost if the agent's reporting buffer fills or a report to the control plane fails.
The agent logs connection report buffer limit reached, dropping report or failed to report connection to server for those reporting failures.
If you start without a session ID, filter the connection log by workspace_owner or workspace_id.
Use connected_after and connected_before with RFC3339 timestamps to limit connection start times.
Both time boundaries are inclusive.
Then take ClientSessionID from the corresponding connection_log service entries.
Read disconnect reasons
When an SSH session or web terminal connection closes, the agent logs disconnect_reason and disconnect_expected and reports the reason to the connection log as DisconnectReason.
The two can differ after a workspace stop or delete: the agent logs server_shutdown, and coderd records workspace_stopped in the connection log.
coderd records workspace_stopped only when a stop or delete build that was requested before the connection closed is running or has succeeded.
Otherwise, the connection log keeps server_shutdown.
While a stop or delete build is pending or running, coderd keeps the workspace agent connected so the agent can report why its connections closed and coderd can look up the build.
As a result, the agent still shows as connected while the workspace is stopping.
disconnect_reason | Meaning |
|---|---|
graceful | The connection ended cleanly, for example when the SSH command exited with status 0. |
network_error | A web terminal connection returned a setup or attachment error, or the agent lost its connection to coderd. SSH sessions don't record this reason. |
server_shutdown | The agent closed the connection because the agent was shutting down. |
workspace_stopped | A workspace stop or delete closed the connection. Only the connection log and the coder ssh log record this reason. |
unknown | The agent couldn't determine why the connection closed. Only the agent log shows unknown; in the connection log, DisconnectReason is empty. |
normal close | A JetBrains Toolbox forwarded channel closed while the agent was running. Only the connection log records this reason. |
For web terminal connections, network_error includes transport errors and errors creating the terminal command.
The agent also records it on disconnected from coderd, with connect_type=server_to_agent.
For SSH sessions, the reason can also be a specific message, such as process exited with error status: 1 when the command exited with a nonzero status, or the raw error text when the session failed.
SSH sessions don't record network_error.
If the agent was shutting down when an SSH session ended, the reason is server_shutdown, and the agent log keeps the specific message in disconnect_detail.
In the agent log, disconnect_expected is true for graceful and server_shutdown.
It's false for network_error, unknown, and specific error messages, including nonzero remote-command exit statuses.
A value of false can describe an ordinary command failure, not a network failure.
connection_log entries don't include disconnect_expected.
When a workspace stop or delete ends a coder ssh session, the coder ssh log has a workspace is shutting down line with disconnect_reason set to workspace_stopped, build_id, build_reason, and transition.
The agent log doesn't include these build fields.
build_reason shows what stopped the workspace:
build_reason | The workspace stopped because |
|---|---|
initiator | A user or API client requested the build. |
autostop | The workspace reached its autostop deadline, its owner was suspended, or a failed start or stop build triggered cleanup. |
dormancy | The workspace became dormant from inactivity. |
autodelete | Coder deleted the dormant workspace automatically, based on the template's schedule. |
Learn more
- Connection logs: filter, export, and retain connection events
- Generate a support bundle: collect client, agent, and deployment diagnostics in one archive
- Networking troubleshooting: diagnose direct and relayed connections with
coder ping