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.*
8.2 KiB
Caddy
This is an example configuration of how to use Coder with caddy. To use Caddy to generate TLS certificates, you'll need a domain name that resolves to your Caddy server.
Getting started
With docker compose
-
Create a
compose.yamlfile and add the following:services: coder: image: ghcr.io/coder/coder:${CODER_VERSION:-latest} environment: CODER_PG_CONNECTION_URL: "postgresql://${POSTGRES_USER:-username}:${POSTGRES_PASSWORD:-password}@database/${POSTGRES_DB:-coder}?sslmode=disable" CODER_HTTP_ADDRESS: "0.0.0.0:7080" # You'll need to set CODER_ACCESS_URL to an IP or domain # that workspaces can reach. This cannot be localhost # or 127.0.0.1 for non-Docker templates! CODER_ACCESS_URL: "${CODER_ACCESS_URL}" # Optional) Enable wildcard apps/dashboard port forwarding CODER_WILDCARD_ACCESS_URL: "${CODER_WILDCARD_ACCESS_URL}" # If the coder user does not have write permissions on # the docker socket, you can uncomment the following # lines and set the group ID to one that has write # permissions on the docker socket. #group_add: # - "998" # docker group on host volumes: - /var/run/docker.sock:/var/run/docker.sock depends_on: database: condition: service_healthy database: image: "postgres:17" ports: - "5432:5432" environment: POSTGRES_USER: ${POSTGRES_USER:-username} # The PostgreSQL user (useful to connect to the database) POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} # The PostgreSQL password (useful to connect to the database) POSTGRES_DB: ${POSTGRES_DB:-coder} # The PostgreSQL default database (automatically created at first launch) volumes: - coder_data:/var/lib/postgresql/data # Use "docker volume rm coder_coder_data" to reset Coder healthcheck: test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-username} -d ${POSTGRES_DB:-coder}", ] interval: 5s timeout: 5s retries: 5 caddy: image: caddy:2.6.2 ports: - "80:80" - "443:443" - "443:443/udp" volumes: - $PWD/Caddyfile:/etc/caddy/Caddyfile - caddy_data:/data - caddy_config:/config volumes: coder_data: caddy_data: caddy_config: -
Create a
Caddyfileand add the following:{ on_demand_tls { ask http://example.com } } coder.example.com, *.coder.example.com { reverse_proxy coder:7080 tls { on_demand issuer acme { email email@example.com } } }Here;
coder:7080is the address of the Coder container on the Docker network.coder.example.comis the domain name you're using for Coder.*.coder.example.comis the domain name for wildcard apps, commonly used for dashboard port forwarding. This is optional and can be removed.email@example.com: Email to request certificates from LetsEncrypt/ZeroSSL (does not have to be Coder admin email)
-
Start Coder. Set
CODER_ACCESS_URLandCODER_WILDCARD_ACCESS_URLto the domain you're using in your Caddyfile.export CODER_ACCESS_URL=https://coder.example.com export CODER_WILDCARD_ACCESS_URL=*.coder.example.com docker compose up -d # Run on startup
Standalone
-
If you haven't already, install Coder
-
Install Caddy Server
-
Copy our sample
Caddyfileand change the following values:{ on_demand_tls { ask http://example.com } } coder.example.com, *.coder.example.com { reverse_proxy coder:7080 }If you're installed Caddy as a system package, update the default Caddyfile with
vim /etc/caddy/Caddyfileemail@example.com: Email to request certificates from LetsEncrypt/ZeroSSL (does not have to be Coder admin email)coder.example.com: Domain name you're using for Coder.*.coder.example.com: Domain name for wildcard apps, commonly used for dashboard port forwarding. This is optional and can be removed.localhost:3000: Address Coder is running on. Modify this if you changedCODER_HTTP_ADDRESSin the Coder configuration.- DO NOT CHANGE the
ask http://example.comline! Doing so will result in your certs potentially not being generated.
-
Configure Coder and change the following values:
CODER_ACCESS_URL: root domain (e.g.https://coder.example.com)CODER_WILDCARD_ACCESS_URL: wildcard domain (e.g.*.example.com).
-
Start the Caddy server:
If you're keeping Caddy running via a system service:
sudo systemctl restart caddyOr run a standalone server:
caddy run -
Optionally, use ufw or another firewall to disable external traffic outside of Caddy.
# Check status of UncomplicatedFirewall sudo ufw status # Allow SSH sudo ufw allow 22 # Allow HTTP, HTTPS (Caddy) sudo ufw allow 80 sudo ufw allow 443 # Deny direct access to Coder server sudo ufw deny 3000 # Enable UncomplicatedFirewall sudo ufw enable -
Navigate to your Coder URL! A TLS certificate should be auto-generated on your first visit.
Generating wildcard certificates
By default, this configuration uses Caddy's
on-demand TLS to
generate a certificate for each subdomain (e.g. app1.coder.example.com,
app2.coder.example.com). When users visit new subdomains, such as accessing
ports on a workspace, the request will
take an additional 5-30 seconds since a new certificate is being generated.
For production deployments, we recommend configuring Caddy to generate a wildcard certificate, which requires an explicit DNS challenge and additional Caddy modules.
-
Install a custom Caddy build that includes the caddy-dns module for your DNS provider (e.g. CloudFlare, Route53).
-
Docker: Build an custom Caddy image with the module for your DNS provider. Be sure to reference the new image in the
compose.yaml. -
Standalone: Download a custom Caddy build with the module for your DNS provider. If you're using Debian/Ubuntu, you can configure the Caddy package to use the new build.
-
-
Edit your
Caddyfileand add the necessary credentials/API tokens to solve the DNS challenge for wildcard certificates.For example, for AWS Route53:
tls { - on_demand - issuer acme { - email email@example.com - } + dns route53 { + max_retries 10 + aws_profile "real-profile" + access_key_id "AKI..." + secret_access_key "wJa..." + token "TOKEN..." + region "us-east-1" + } }Configuration reference from caddy-dns/route53.
And for CloudFlare:
Generate a token with the following permissions:
- Zone:Zone:Edit
tls { - on_demand - issuer acme { - email email@example.com - } + dns cloudflare CLOUDFLARE_API_TOKEN }Configuration reference from caddy-dns/cloudflare.