Files
coder/docs/admin/templates/extending-templates/process-logging.md
T
Nick Vigilante c84aa564ba docs: normalize code-fence languages for Shiki compatibility (#27161)
Normalizes non-standard code-fence language tags across `docs/**` so a
strict highlighter (Shiki, used by Fumadocs) won't fail the build on an
unrecognized language, and unifies redundant synonym tags onto one
canonical form per language. The current renderer (Speed-Highlight)
detects the language from the code content, not the fence label, so this
drift wasn't visible until now.

## Changes

- `hcl` -> `tf` (199 fences, including indented ones nested in
numbered/bulleted lists). Shiki ships `hcl` and `terraform` as two
distinct grammars (not aliases); every `hcl`-tagged fence in `docs/**`
is actually Terraform resource/data/provider syntax, so the more
specific `terraform` grammar is correct for all of them. `tf` is Shiki's
own alias for that grammar, and it's also what GitHub's own markdown
renderer resolves to the same HCL/Terraform highlighting.
- `pwsh`/`powershell` -> `ps1`. Both `ps` and `ps1` are registered
PowerShell aliases in Shiki, but on GitHub's renderer only `.ps1` is a
registered file extension (`.ps` isn't), so `ps1` renders identically to
`powershell` there today while bare `ps` would silently lose
highlighting.
- `env` -> `dotenv` (a dedicated Shiki grammar for `KEY=VALUE` files)
- `text`/`output`/`none`/`url` -> `txt`. Same built-in plain-text
fallback either way, just shorter.
- `Dockerfile` -> `dockerfile` (lowercase)
- `bash`/`shell` -> `sh` (732 fences). Shiki and GitHub both alias all
three to a single shell grammar; this was already the style guide's
stated preference, just not enforced across the existing corpus until
now.
- `markdown` -> `md` (4 fences). Alias of the same grammar in both Shiki
and GitHub.
- `jsonc` -> `json` (1 fence). The block has no comments or trailing
commas, so it doesn't need the comments-capable grammar.
- `ts` -> `tsx` (2 fences, `docs/about/contributing/frontend.md`).
Verified the actual content tokenizes identically under both grammars,
and a sibling block in the same file already needs `tsx` for real JSX,
so unifying to one tag is safe for this file. Documented a caveat: `tsx`
mis-tokenizes the legacy angle-bracket type-assertion syntax
(`<Type>value`), which is invalid in real `.tsx` files anyway, so use
`value as Type` instead.
- `yml` -> `yaml` (1 fence)
- Updated `docs/.style/style-guide/formatting.md` to document all
canonical tags

`promql` (2 fences) and `caddyfile` (2 fences) are left as-is. Shiki
doesn't bundle a grammar for either, so they need a custom grammar
registration when the site adopts Shiki, rather than degrading to `txt`.
Tracked as follow-up work under DOCS-118 and
[DOCS-544](https://linear.app/codercom/issue/DOCS-544/vendor-a-local-promql-grammar-for-shiki-syntax-highlighting)
(promql).

Does not touch `offlinedocs/`.

Linear:
[DOCS-476](https://linear.app/codercom/issue/DOCS-476/normalize-docs-code-fence-languages-de-risk-shikifumadocs)

<details>
<summary>How the fence tags were verified</summary>

Each tag was tested against a real `shiki@latest` highlighter instance
(`codeToHtml`/`codeToTokens`) and cross-checked against GitHub's
`@wooorm/starry-night` grammar sources (the renderer that actually
displays these `.md` files today, in repo browsing and PR diffs), since
that's what determines whether brevity is safe before Shiki adoption:

```text
FAIL  env        -- Language `env` is not included in this bundle.
FAIL  Dockerfile -- Language `Dockerfile` is not included in this bundle.
FAIL  promql     -- Language `promql` is not included in this bundle.
FAIL  caddyfile  -- Language `caddyfile` is not included in this bundle.
FAIL  pwsh       -- Language `pwsh` is not included in this bundle.
FAIL  output     -- Language `output` is not included in this bundle.
```

`hcl` doesn't error in Shiki, since it's a real grammar, but that's
exactly the trap: it was silently rendering every fence with the generic
HCL grammar instead of the Terraform-specific one. Every `hcl`-tagged
fence in `docs/**` was manually checked against `origin/main` and is
genuinely Terraform content.

For `ts`/`tsx`, tokenizing the actual doc content confirmed identical
output under both grammars; a synthetic test with the legacy
angle-bracket cast syntax confirmed `tsx` degrades on that specific
construct, which the style guide now calls out.

The first normalization pass only matched fence tags at column 0
(`^```tag$`), missing tags indented inside numbered/bulleted lists. A
follow-up pass caught the remaining occurrences at any indentation
level.

</details>


---

*This PR description and the underlying changes were prepared with Coder
Agents assistance.*
2026-07-15 14:07:09 -04:00

9.9 KiB

Workspace Process Logging

The workspace process logging feature allows you to log all system-level processes executing in the workspace.

This feature is only available on Linux in Kubernetes. There are additional requirements outlined further in this document.

Note

Workspace process logging is a Premium feature. Learn more.

Workspace process logging adds a sidecar container to workspace pods that will log all processes started in the workspace container (e.g., commands executed in the terminal or processes created in the background by other processes). Processes launched inside containers or nested containers within the workspace are also logged. You can view the output from the sidecar or send it to a monitoring stack, such as CloudWatch, for further analysis or long-term storage.

Please note that these logs are not recorded or captured by the Coder organization in any way, shape, or form.

How this works

Coder uses eBPF (which we chose for its minimal performance impact) to perform in-kernel logging and filtering of all exec system calls originating from the workspace container.

The core of this feature is also open source and can be found in the exectrace GitHub repo. The enterprise component (in the enterprise/ directory of the repo) is responsible for starting the eBPF program with the correct filtering options for the specific workspace.

Requirements

The host machine must be running a Linux kernel >= 5.8 with the kernel config CONFIG_DEBUG_INFO_BTF=y enabled.

To check your kernel version, run:

uname -r

To validate the required kernel config is enabled, run either of the following commands on your nodes directly (not from a workspace terminal):

cat /proc/config.gz | gunzip | grep CONFIG_DEBUG_INFO_BTF
cat "/boot/config-$(uname -r)" | grep CONFIG_DEBUG_INFO_BTF

If these requirements are not met, workspaces will fail to start for security reasons.

Your template must be a Kubernetes template. Workspace process logging is not compatible with the sysbox-runc runtime due to technical limitations, but it is compatible with our envbox template family.

Example templates

We provide working example templates for Kubernetes, and Kubernetes with envbox (for Docker support in workspaces). You can view these templates in the exectrace repo.

Configuring custom templates to use workspace process logging

If you have an existing Kubernetes or Kubernetes with envbox template that you would like to add workspace process logging to, follow these steps:

  1. Ensure the image used in your template has curl installed.

  2. Add the following section to your template's main.tf file:

    locals {
      # This is the init script for the main workspace container that runs before the
      # agent starts to configure workspace process logging.
      exectrace_init_script = <<EOT
        set -eu
        pidns_inum=$(readlink /proc/self/ns/pid | sed 's/[^0-9]//g')
        if [ -z "$pidns_inum" ]; then
          echo "Could not determine process ID namespace inum"
          exit 1
        fi
    
        # Before we start the script, does curl exist?
        if ! command -v curl >/dev/null 2>&1; then
          echo "curl is required to download the Coder binary"
          echo "Please install curl to your image and try again"
          # 127 is command not found.
          exit 127
        fi
    
        echo "Sending process ID namespace inum to exectrace sidecar"
        rc=0
        max_retry=5
        counter=0
        until [ $counter -ge $max_retry ]; do
          set +e
          curl \
            --fail \
            --silent \
            --connect-timeout 5 \
            -X POST \
            -H "Content-Type: text/plain" \
            --data "$pidns_inum" \
            http://127.0.0.1:56123
          rc=$?
          set -e
          if [ $rc -eq 0 ]; then
            break
          fi
    
          counter=$((counter+1))
          echo "Curl failed with exit code $${rc}, attempt $${counter}/$${max_retry}; Retrying in 3 seconds..."
          sleep 3
        done
        if [ $rc -ne 0 ]; then
          echo "Failed to send process ID namespace inum to exectrace sidecar"
          exit $rc
        fi
    
      EOT
    }
    
  3. Update the command of your workspace container like the following:

    resource "kubernetes_pod" "main" {
      ...
      spec {
        ...
        container {
          ...
          // NOTE: this command is changed compared to the upstream kubernetes
          // template
          command = [
            "sh",
            "-c",
            "${local.exectrace_init_script}\n\n${coder_agent.main.init_script}",
          ]
          ...
        }
        ...
      }
      ...
    }
    

    Note

    If you are using the envbox template, you will need to update the third argument to be "${local.exectrace_init_script}\n\nexec /envbox docker" instead.

  4. Add the following container to your workspace pod spec.

    resource "kubernetes_pod" "main" {
      ...
      spec {
        ...
        // NOTE: this container is added compared to the upstream kubernetes
        // template
        container {
          name              = "exectrace"
          image             = "ghcr.io/coder/exectrace:latest"
          image_pull_policy = "Always"
          command = [
            "/opt/exectrace",
            "--init-address", "127.0.0.1:56123",
            "--label", "workspace_id=${data.coder_workspace.me.id}",
            "--label", "workspace_name=${data.coder_workspace.me.name}",
            "--label", "user_id=${data.coder_workspace_owner.me.id}",
            "--label", "username=${data.coder_workspace_owner.me.name}",
            "--label", "user_email=${data.coder_workspace_owner.me.email}",
          ]
          security_context {
            // exectrace must be started as root so it can attach probes into the
            // kernel to record process events with high throughput.
            run_as_user  = "0"
            run_as_group = "0"
            // exectrace requires a privileged container so it can control mounts
            // and perform privileged syscalls against the host kernel to attach
            // probes.
            privileged = true
          }
        }
        ...
      }
      ...
    }
    

    Note

    exectrace requires root privileges and a privileged container to attach probes to the kernel. This is a requirement of eBPF.

  5. Add the following environment variable to your workspace pod:

    resource "kubernetes_pod" "main" {
      ...
      spec {
        ...
        env {
          name = "CODER_AGENT_SUBSYSTEM"
          value = "exectrace"
        }
        ...
      }
      ...
    }
    

Once you have made these changes, you can push a new version of your template and workspace process logging will be enabled for all workspaces once they are restarted.

Viewing workspace process logs

To view the process logs for a specific workspace you can use kubectl to print the logs:

kubectl logs pod-name --container exectrace

The raw logs will look something like this:

{
    "ts": "2022-02-28T20:29:38.038452202Z",
    "level": "INFO",
    "msg": "exec",
    "fields": {
        "labels": {
            "user_email": "jessie@coder.com",
            "user_id": "5e876e9a-121663f01ebd1522060d5270",
            "username": "jessie",
            "workspace_id": "621d2e52-a6987ef6c56210058ee2593c",
            "workspace_name": "main"
        },
        "cmdline": "uname -a",
        "event": {
            "filename": "/usr/bin/uname",
            "argv": ["uname", "-a"],
            "truncated": false,
            "pid": 920684,
            "uid": 101000,
            "gid": 101000,
            "comm": "bash"
        }
    }
}

View logs in AWS EKS

If you're using AWS' Elastic Kubernetes Service, you can configure your cluster to send logs to CloudWatch. This allows you to view the logs for a specific user or workspace.

To view your logs, go to the CloudWatch dashboard (which is available on the Log Insights tab) and run a query similar to the following:

fields @timestamp, log_processed.fields.cmdline
| sort @timestamp asc
| filter kubernetes.container_name="exectrace"
| filter log_processed.fields.labels.username="zac"
| filter log_processed.fields.labels.workspace_name="code"

Usage considerations

  • The sidecar attached to each workspace is a privileged container, so you may need to review your organization's security policies before enabling this feature. Enabling workspace process logging does not grant extra privileges to the workspace container itself, however.
  • exectrace will log processes from nested Docker containers (including deeply nested containers) correctly, but Coder does not distinguish between processes started in the workspace and processes started in a child container in the logs.
  • With envbox workspaces, this feature will detect and log startup processes begun in the outer container (including container initialization processes).
  • Because this feature logs all processes in the workspace, high levels of usage (e.g., during a make run) will result in an abundance of output in the sidecar container. Depending on how your Kubernetes cluster is configured, you may incur extra charges from your cloud provider to store the additional logs.