Files
coder/docs/admin/templates/extending-templates/modules.md
T
Nick Vigilante ed908ed019 fix(docs): repoint 7 broken external and anchor links (DOCS-415) (#26572)
Closes [DOCS-415](https://linear.app/codercom/issue/DOCS-415).

## TL;DR

Repoints 7 broken links across 5 docs files that the 2026-06-22 weekly
`check-docs.yml` Linkspector run flagged. Two other links from the same
run (the dead `nix` ref and the dead `reflectoring.io` ref in
`CONTRIBUTING.md`) were already folded into
[#26341](https://github.com/coder/coder/pull/26341).

## Why

Broken external and anchor links degrade reader trust, leak SEO juice,
and make the docs look stale. The weekly `check-docs` job exists
precisely to catch this kind of rot before customers do; the
surfacing-to-fix turnaround on these 7 is one PR. Run that surfaced
them: [actions/runs/27948011619 job
82697664858](https://github.com/coder/coder/actions/runs/27948011619/job/82697664858).

## Scope

| File | Line(s) | Old target | New target | Why |
|------|---------|-----------|------------|-----|
| `docs/tutorials/best-practices/organizations.md` | 62 | anchor
`#update-template-metadata-by-id` | `#update-template-settings-by-id` |
API endpoint renamed in
[#19228](https://github.com/coder/coder/pull/19228) (Aug 2025). New
heading at line 1105 of `docs/reference/api/templates.md`. |
| `docs/install/registry-mirror-artifactory.md` | 197 | JFrog
`terraform-registry` |
`terraform-opentofu-and-terraform-backend-repositories` | JFrog
consolidated their Terraform / OpenTofu / Backend docs into a single
page. |
| `docs/admin/templates/extending-templates/modules.md` | 76, 206 |
JFrog `set-up-a-terraform-module/provider-registry` and
`terraform-registry` | same consolidated JFrog page (root, no anchor) |
Same JFrog consolidation. Anchor dropped, see decision log. |
| `docs/admin/integrations/dx-data-cloud.md` | 84 |
`https://help.getdx.com/en/` | `https://docs.getdx.com/` | DX migrated
their help center to a separate docs domain. |
| `docs/about/contributing/frontend.md` | 37, 71 |
`https://reactrouter.com/en/main` | `https://reactrouter.com/` | React
Router dropped the `/en/main` prefix. |

## Validation

- All 7 replacement URLs return HTTP 200 (manual `curl -L -o /dev/null
-w '%{http_code}'` per URL; linkspector's puppeteer crashed in the agent
env, so it was run case-by-case)
- `make lint/markdown lint/emdash` clean locally
- Pre-commit hook (`scripts/githooks/pre-commit` -> `make
pre-commit-light`) clean
- No `/docs/` route changes; pure markdown content

## Not triggering `/coder-agents-review`

Docs-only markdown edit, no CI or build config changes; per `AGENTS.md`
the bot review is reserved for product / CI changes. `doc-check` handles
this category.

## Pre-mortem

| Concern | Mitigation |
|---|---|
| Replacement URL also turns out to be broken later | All 7 verified
HTTP 200 today; next weekly `check-docs` run will catch any future
regression. |
| JFrog anchor drop on `modules.md` (76, 206) loses navigation context |
Verified the consolidated JFrog page has no clean section anchor for the
original target; linking the root page is the honest fix. If JFrog ships
a better TOC anchor later, a follow-up can reattach. |
| Anchor rename in `organizations.md` was actually a different rename |
Confirmed via PR #19228 (Aug 2025) which is the exact rename that
produced `## Update template settings by ID`. |

<details>
<summary>Decision log</summary>

**Why drop the anchor on the JFrog `modules.md` links (76 + 206)**:
JFrog's new consolidated page
(`/terraform-opentofu-and-terraform-backend-repositories`) doesn't
expose the original `set-up-a-terraform-module/provider-registry`
section as a fragment-link target. The honest fix is to link the page
root; readers can scroll. The `registry-mirror-artifactory.md:197`
reference uses the same root link for symmetry.

**Why DX `docs.getdx.com` over `help.getdx.com`**: DX's help center at
`help.getdx.com/en/` now returns 404. They moved to a separate
`docs.getdx.com` domain with a different content structure. Linking the
docs root is the closest analog to the original "browse our docs"
intent.

**Why React Router root over `/en/main`**: React Router unified their
docs under the root URL. The `/en/main` prefix is no longer routable.
The root URL is the canonical successor.

</details>


<details>
<summary>CI: <code>audit-docs-paths</code> failure (pre-existing,
unrelated)</summary>

The `audit-docs-paths` job in `.github/workflows/weekly-docs.yaml` fails
on this PR because its `Fetch redirects.json` step issues an
unauthenticated `curl` to a file in private `coder/coder.com` and gets a
404 (exit code 22). Same failure on every recent PR in this repo.
Tracked in [DOCS-409](https://linear.app/codercom/issue/DOCS-409) and
fixed in [#26571](https://github.com/coder/coder/pull/26571), which
authenticates the fetch through the Contents API. My changes are
docs-content only (5 markdown files, 7 line changes) and don't touch the
TS/TSX paths or `redirects.json` that the audit examines, so this is a
pre-existing CI break, not a regression introduced here.

</details>

---

*Generated by Coder Agents on @nickvigilante's behalf.*
2026-06-22 17:04:19 -04:00

6.7 KiB

Reusing template code

To reuse code across different Coder templates, such as common scripts or resource definitions, we suggest using Terraform Modules.

You can store these modules externally from your Coder deployment, like in a git repository or a Terraform registry. This example shows how to reference a module from your template:

data "coder_workspace" "me" {}

module "coder-base" {
  source = "github.com/my-organization/coder-base"

  # Modules take in variables and can provision infrastructure
  vpc_name            = "devex-3"
  subnet_tags         = { "name": data.coder_workspace.me.name }
  code_server_version = 4.14.1
}

resource "coder_agent" "dev" {
  # Modules can provide outputs, such as helper scripts
  startup_script=<<EOF
  #!/bin/sh
  ${module.coder-base.code_server_install_command}
  EOF
}

Learn more about creating modules and module sources in the Terraform documentation.

Coder modules

Coder publishes plenty of modules that can be used to simplify some common tasks across templates. Some of the modules we publish are,

  1. code-server and vscode-web
  2. git-clone
  3. dotfiles
  4. jetbrains
  5. jfrog-oauth and jfrog-token
  6. vault-github

For a full list of available modules please check Coder module registry.

Offline installations

In offline and restricted deployments, there are three ways to fetch modules.

  1. Artifactory Remote Terraform Repository (Recommended)
  2. Artifactory Local Repository (manual publishing)
  3. Private git repository

Configure Artifactory as a Remote Terraform Repository that proxies and caches the Coder registry. This approach provides automatic updates and requires no manual synchronization.

See Mirror the Coder Registry with JFrog Artifactory for complete setup instructions.

Artifactory Local Repository

Air-gapped users can clone the coder/registry repo and publish a local terraform module repository to resolve modules via Artifactory.

  1. Create a local-terraform-repository with name coder-modules-local

  2. Create a virtual repository with name tf

  3. Follow the below instructions to publish coder modules to Artifactory

    git clone https://github.com/coder/registry
    cd registry/registry/coder/modules
    jf tfc
    jf tf p --namespace="coder" --provider="coder" --tag="1.0.0"
    
  4. Generate a token with access to the tf repo and set an ENV variable TF_TOKEN_example.jfrog.io="XXXXXXXXXXXXXXX" on the Coder provisioner.

  5. Create a file .terraformrc with following content and mount at /home/coder/.terraformrc within the Coder provisioner.

    provider_installation {
      direct {
          exclude = ["registry.terraform.io/*/*"]
      }
      network_mirror {
          url = "https://example.jfrog.io/artifactory/api/terraform/tf/providers/"
      }
    }
    
  6. Update module source as:

    module "module-name" {
      source = "https://example.jfrog.io/tf__coder/module-name/coder"
      version = "1.0.0"
      agent_id = coder_agent.example.id
      ...
    }
    

    Replace example.jfrog.io with your Artifactory URL

Based on the instructions here.

Example template

We have an example template here that uses our JFrog Docker template as the underlying module.

Private git repository

If you are importing a module from a private git repository, the Coder server or provisioner needs git credentials. Since this token will only be used for cloning your repositories with modules, it is best to create a token with access limited to the repository and no extra permissions. In GitHub, you can generate a fine-grained token with read only access to the necessary repos.

If you are running Coder on a VM, make sure that you have git installed and the coder user has access to the following files:

# /home/coder/.gitconfig
[credential]
  helper = store
# /home/coder/.git-credentials

# GitHub example:
https://your-github-username:your-github-pat@github.com

If you are running Coder on Docker or Kubernetes, git is pre-installed in the Coder image. However, you still need to mount credentials. This can be done via a Docker volume mount or Kubernetes secrets.

Passing git credentials in Kubernetes

First, create a .gitconfig and .git-credentials file on your local machine. You might want to do this in a temporary directory to avoid conflicting with your own git credentials.

Next, create the secret in Kubernetes. Be sure to do this in the same namespace that Coder is installed in.

export NAMESPACE=coder
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: git-secrets
  namespace: $NAMESPACE
type: Opaque
data:
  .gitconfig: $(cat .gitconfig | base64 | tr -d '\n')
  .git-credentials: $(cat .git-credentials | base64 | tr -d '\n')
EOF

Then, modify Coder's Helm values to mount the secret.

coder:
  volumes:
    - name: git-secrets
      secret:
        secretName: git-secrets
  volumeMounts:
    - name: git-secrets
      mountPath: "/home/coder/.gitconfig"
      subPath: .gitconfig
      readOnly: true
    - name: git-secrets
      mountPath: "/home/coder/.git-credentials"
      subPath: .git-credentials
      readOnly: true

Next steps