Why teams move Backstage TechDocs builds into CI with techdocs-cli
Teams move TechDocs builds into CI when the portal backend can no longer carry the cost, timing and credential exposure of building MkDocs sites on demand. In the recommended setup, the CI system generates each docs site and publishes it to cloud storage, and techdocs-cli handles both steps. The backend then only reads finished sites, so a slow or failed MkDocs build no longer ties up the portal, and the backend does not need permission to write documentation.
The two commands split the work. techdocs-cli generate runs the MkDocs build for a docs project and produces the static site, along with a content hash (an etag) that identifies the output. techdocs-cli publish uploads that site to the configured storage target, and with --skip-if-unchanged it compares the hash with the version already published and skips the upload when they match. A monorepo with many documented components, or a docs site whose builds run often without content changes, benefits most from that skip. Backend generation still fits small installations with a handful of docs projects, local development, or teams that accept builds happening during a page view.
Before you choose, confirm three things with your own environment: which storage target you will use (see the TechDocs storage topic), which CI system holds the write credential, and how many docs projects you will build per pipeline run. Your team should also decide who owns the MkDocs plugins and versions used in CI, because a build that differs from what the backend would have produced will surprise readers. The TechDocs CI/CD guide sets out the expected pipeline steps, and the TechDocs architecture page explains the split between writer and reader that makes this model work.
Which publishing options fit which setup
This path runs techdocs-cli generate in a pull request or validation job to confirm that MkDocs builds without errors, then stops before any upload. It fits teams that want docs checked for broken navigation, missing pages and plugin failures without publishing anything yet, or teams whose publishing step runs in a separate pipeline they do not control. The cost is mostly CI compute for the build itself, with no storage writes and no change visible to readers.
- Build minutes: One MkDocs build per run, so cost grows with how often pull requests touch docs and how large the site is.
- Storage writes: None. Nothing reaches the bucket, so the published site does not change.
- Freshness: Readers see nothing new. This path verifies content but does not keep the portal current.
- Best use: Gating documentation changes on a passing build before they are merged.
- Gap to close: A separate publish step must exist somewhere, or the validated docs will never reach the portal.
Check that your pipeline records the build result and that someone owns the publish step that follows it.
This path runs techdocs-cli generate followed by techdocs-cli publish with --skip-if-unchanged. The CLI computes a content hash (etag) of the generated site and skips the upload when that hash matches the version already published. It fits most teams with a mix of frequent and occasional docs changes, because builds still run on every merge but storage is written only when the output actually differs.
- Build minutes: One generate step per merge. The skip check happens after the build, so compute cost is similar to the full publish path.
- Storage writes: Only changed sites are uploaded, which reduces writes and object churn for entities whose docs did not change.
- Freshness: Changed docs appear as soon as the job finishes, with no delay from the skip logic.
- Best use: Monorepos or many entities where most merges touch code but not docs.
- Confirm: Whether the hash also covers assets and generated metadata in your setup, and how your CI cache affects the comparison.
Test the skip behavior on a repeat run with no docs changes to confirm the upload is actually skipped.
This path generates and uploads the site on every merge to the default branch, without a comparison step. It is the simplest pipeline to reason about: whatever was merged is what gets published. It fits small portfolios, teams that prefer an unconditional publish for auditability, or early setups where the skip logic is not yet worth adding.
- Build minutes: One generate and one upload per merge, regardless of whether the content changed.
- Storage writes: Every affected site is rewritten on every merge, which increases write volume on the bucket.
- Freshness: Readers get the newest content on every run, with no chance of a stale hash match.
- Best use: Small catalogs with few docs repositories, or environments where a clear publish log per merge is required.
- Watch for: Storage write and request charges grow with merge frequency, and the bucket layout should be reviewed before scale.
Ask your storage team which write and listing costs apply to your provider and how old versions are retained.
This path runs TechDocs generation inside the Backstage backend on a developer machine, so authors see their changes in the portal before merging. It is useful for writing and reviewing docs, but it is not a production publishing path. The backend generates the site locally and serves it directly, which means nothing is written to the production bucket and the output is not shared with other readers.
- Build minutes: None in CI. The cost falls on the developer's machine and on the local backend process.
- Storage writes: None to shared storage. Output stays on the local filesystem or local preview.
- Freshness: Immediate for the author, but invisible to everyone else until a CI publish runs.
- Best use: Authoring, reviewing layout and checking plugin output before opening a pull request.
- Limit: Production readers must never depend on this path, and the backend should not hold write credentials for the production bucket.
Confirm the local generation settings match the CI build so previews do not differ from what gets published.
Setting up techdocs-cli in CI step by step
This sequence moves TechDocs generation out of the Backstage server and into your CI pipeline, with separate write and read credentials, so each published docs site is built once and served from storage the portal can read.
Point the Backstage app at your storage location
The platform team edits the TechDocs section of the app configuration so the techdocs-backend plugin reads from your chosen bucket or container instead of local storage. Set the publisher type and the bucket or container name in the configuration file for your environment, and leave out any write credentials from this file. Confirm the exact configuration keys for your storage type against the TechDocs architecture documentation for your Backstage version. You know this step is done when the backend starts without publisher errors and the TechDocs tab of a test entity loads an empty or placeholder state instead of an error.
Create a CI write credential
The platform or DevOps team creates a credential that only the CI system holds, with permission to upload objects to the TechDocs storage location and nothing else. Store it as a secret in your CI platform, not in the Backstage repository. Ask your security team to confirm the scope, rotation schedule and who can read the secret. You know this step is done when a test upload from a CI job succeeds and the same credential is rejected for any other bucket or container you own.
Create a separate read-only credential for techdocs-backend
The platform team creates a second credential with read-only access to the same storage location and gives it to techdocs-backend through its secret store or environment. The backend only needs to fetch published sites, so it should never be able to write or delete. Keep this credential separate from the CI credential so that revoking one does not affect the other. You know this step is done when the backend can load a published site and a write attempt made with this credential is denied.
Add generate and publish commands to the pipeline
The team that owns the docs pipeline adds a job that installs techdocs-cli, runs the generate step against the repository's MkDocs site, and then runs the publish step with the write credential. Generate builds the static site and computes a content hash (etag) of the output, and publish uploads it to the storage location for the entity. Check the exact flags for your CLI version in the TechDocs CI/CD guide. You know this step is done when a pipeline run on a docs change logs a successful upload to the expected path.
Enable skip-if-unchanged on publish
The same team adds the skip-if-unchanged option to the publish step so the upload is skipped when the hash of the generated site matches the version already published. This avoids rewriting identical content on every pipeline run, which keeps storage writes and cache churn down. Confirm that your pipeline still fails loudly when generation itself fails, because a skipped publish should only happen on a genuine match. You know this step is done when two consecutive runs with no docs changes log a skip and the second run uploads nothing.
Verify that a sample entity's docs update in the portal
Choose one catalog entity whose repository has a TechDocs site and make a visible change to its documentation, such as a heading or a sentence. Run the pipeline, wait for the publish step to complete, then open the entity's docs tab in the portal and confirm the change appears. Repeat with an unchanged commit to confirm the skip path leaves the page as it was. You know this step is done when the updated content displays in Backstage and the sample entity's build log matches what you expected, and any mismatch is traced to the publisher configuration, credentials or pipeline job before you roll out to other entities.
How the content hash and skip-if-unchanged option avoid redundant uploads
The `techdocs-cli generate` step computes an etag, a content hash over the generated site, and `publish` with `--skip-if-unchanged` compares that hash with the version already in storage. If they match, the upload is skipped. This matters in CI because most merges touch one repository's source files, yet a naive pipeline would re-upload every docs site on every build. Skipping unchanged sites keeps storage writes low, shortens pipeline runs for large catalogs, and avoids churning the objects the backend is serving. The check only works if the hash is computed over the full generated output, not just the Markdown you edited, so the reader should confirm in the TechDocs CI/CD guide exactly which inputs the hash covers in the CLI version they run.
The hash changes whenever the generated site changes. That includes Markdown and asset content, the MkDocs configuration, and anything the generator produces from them. It can also change when the TechDocs generator, the MkDocs theme, or a plugin used during the build changes version, even though no documentation text was edited. Teams that pin tooling in one place and let the CI image float will see unexpected re-publishes, or, in the other direction, may assume a tooling upgrade changed the site when it did not. Treat the generator image and CLI version as inputs to the build, record them, and plan a deliberate full republish after any change to them so that every site reflects the new tooling.
The main risk is a stale site. If the pipeline runs publish without --skip-if-unchanged in some jobs but not others, or if a build step writes output the hash does not see, the stored site can lag behind the source while the pipeline reports success. Before rollout, run a generate and publish cycle on one non-critical repository, change a page and a theme setting, and confirm the new content appears in the reader. Then confirm that an unchanged rebuild logs a skip. Document who owns the CI write token, and set an occasional full publish, such as after each Backstage upgrade, so the stored sites never drift far from what the current toolchain would produce.
Securing write tokens, read access and the storage bucket
Moving TechDocs generation into CI splits the job into two trust zones: the pipeline that writes sites and the backend that only reads them. The cards below cover the controls that keep those zones separate, from token scope and log hygiene to bucket listing, rotation and the sensitivity of the docs themselves.
Scope the CI write token to one target
The CI system holds the only credential that can write generated sites, so its scope should be as narrow as the storage platform allows. Limit it to the TechDocs bucket or prefix used by publish, and to the write actions that publish needs. Check that the same token cannot write to other buckets, read unrelated data or manage storage configuration. Confirm the exact permission model with your storage vendor, because each provider names these permissions differently.
Keep the write token out of logs and pull requests
Generation and publish steps print configuration and command output, and a token passed as a flag or environment value can end up in build logs, artifacts or forked pull request runs. Inject the credential from the CI secret store, mask it in logs, and do not run publish on untrusted pull requests that can read secrets. Review the pipeline definition as code, since a change to a workflow file is a change to who can publish.
Give techdocs-backend read-only access
The backend only needs to fetch published sites and serve them to readers, so its storage identity should have read permissions and nothing else. Keeping it read-only means a compromised Backstage instance cannot overwrite or delete documentation. Keep the backend and CI credentials separate, with different identities, so that rotating one does not disturb the other. Verify the backend identity by attempting a write in a test environment and confirming it fails.
Restrict bucket listing to what readers need
Public listing of a documentation bucket reveals entity names, version paths and file structure even when page content is protected. Allow object reads only where the backend needs them and deny bucket-level listing to anonymous callers. Ask your storage team whether listing is needed by the backend for its lookups, and test that a direct request to the bucket without credentials returns no site contents.
Rotate credentials on a set schedule
Write tokens and backend keys should have an owner, an expiry and a rotation runbook. Plan rotation so that CI secrets and the backend configuration change together, otherwise publish or reads will fail during the switch. Record each rotation and revoke old credentials once the new ones are confirmed. Ask your platform team which rotation interval your policy requires and whether your storage provider supports short-lived or federated credentials for CI runners.
Treat docs content as sensitive
Architecture notes, runbooks, incident write-ups and internal hostnames often end up in TechDocs sites, and the published output is only as protected as the storage and backend allow. Classify documentation repositories by sensitivity before connecting them to CI, and decide which entities may publish externally. Review what a published site exposes, because the controls in this section protect the storage path but do not decide what content is appropriate to publish.
Questions teams ask before moving TechDocs builds into CI
What does moving TechDocs builds into CI cost, and who pays for what?
The cost is mostly the engineering time to change the build, and it is usually a one-time project cost, with small recurring costs for CI minutes and object storage. Your platform or DevOps team pays for the pipeline changes, token management and storage setup, and your docs owners pay for checking their sites after the switch. A partner quote should separate the one-time work (pipeline design, a reusable CI template, storage and token setup, testing) from any retainer for later maintenance. Ask which items are fixed in the written scope and which depend on how many repositories publish docs. Storage and CI runner costs recur, so ask who owns them after handover.
How long does a TechDocs CI migration take?
Duration depends mostly on how many repositories publish docs, how varied their build setups are, and how many storage and CI systems are involved. A team with a few sites using one CI platform and one bucket is a small change. A larger estate with mixed MkDocs plugins, several CI systems and separate access rules takes longer because each site needs a build, a publish step and a check. Ask any provider for a timeline tied to a written list of repositories and the target CI platform. This page does not give a duration, so confirm the figure against your own inventory.
Can one internal engineer move TechDocs into CI alone?
One engineer who knows both the CI platform and the storage bucket can usually handle a small set of sites, but the work has three parts that rarely sit with one person: pipeline changes, storage and access policy, and Backstage configuration. The techdocs-cli generate and publish commands are straightforward, so the harder parts are write token scope, bucket permissions for the backend and reviewing each site's MkDocs plugins. Plan a second reviewer for the access model, because a write token that is too broad is the most common security gap. If your team lacks one of these skills, a partner can cover that part within a fixed scope.
What access and approvals does the team need to provide?
The work needs four kinds of access and sign-off:
- Read access to every repository that publishes docs, and write access to the pipelines that will build them.
- Admin or maintainer rights in the CI system to add secrets, runners and a shared pipeline template.
- Create and policy rights on the object storage bucket, so you can grant the CI system a write-capable token and the
techdocs-backendread-only access. - A named owner who approves the token scope and the storage policy.
Ask your security team to review the token lifetime and rotation before the pipelines are wired up, since CI secrets are often reachable by many jobs.
What happens when a TechDocs site stops updating after the move?
A site that stops updating usually has one of a few causes: the CI job failed or was skipped, the pipeline no longer runs on that repository's branch, the write token expired or was rotated, or the content hash matched and the upload was skipped as intended. Check the CI job log first, then confirm the token is valid and the publish step ran. The --skip-if-unchanged option compares the generated site's content hash with the published version, so a site may look stale when the content really did not change. Compare the hash in the build log with the one stored in the bucket before you assume a fault. Add an alert for failed docs jobs so a stale site is noticed early.
Does the move change who can read the published docs?
The move does not change reader access by itself, but it moves write access into CI, so the access model needs to be decided on purpose. The backend should only need read access to the bucket, and the CI system should hold a write-capable token scoped to the docs prefix. Reader visibility in Backstage is controlled separately by catalog permissions. Before you cut over, confirm that the bucket is not publicly readable unless the docs are meant to be public. Treat the write token as a production secret, and document who can change its scope.
Which parts of the migration should we check before cutting over?
Check the output, the access and the failure path, in that order. For output, build each site with techdocs-cli generate in CI and compare it with the current result in the Backstage reader. For access, confirm the CI token can write only to the expected location and that the backend can read but not write. For failure paths, deliberately break a build and confirm that the job fails visibly and the previous version stays published. Keep the old server-side build available until every site has passed these checks. Record each result in your change log, since auditors and reviewers often ask how documentation changes are controlled.
How this connects to storage, visibility and caching decisions
Publishing from CI makes the storage choice a prerequisite for the publish step, not an afterthought. The techdocs-cli publish command uploads the generated site to whatever bucket or container your publisher is configured for, so the CI job needs a target that exists, a write-capable credential scoped to that target, and a naming scheme the backend can read back. The recommended pattern in the TechDocs architecture keeps the write token in CI and gives techdocs-backend read access only. Before you finalize the pipeline, confirm which storage backend your organization will use, who owns the bucket, and how credentials are rotated, because those answers determine the publish configuration you write now. Storage placement and access separation are covered in the sibling topic on choosing TechDocs storage.
Visibility is a separate control that the publish pipeline does not enforce. Anyone who can reach a published site through the portal can read it, and whether a given team sees a given entity's docs depends on catalog permissions that TechDocs follows, not on how the site was built. A site published with a correct content hash and a clean pipeline can still be visible to the wrong audience if catalog read rules are left open. Decide which docs are internal before the first publish, and confirm with your Backstage team that the permission framework is enabled for the TechDocs entities you plan to publish. The sibling topic on restricting TechDocs visibility with catalog permissions sets out those rules.
Caching affects how quickly a published page loads once the docs live in remote object storage, and it does not change what gets published. Each page request from the portal fetches files from the bucket, so a CI publish pattern that produces many small files or frequent re-publishes can increase latency for readers. Ask your platform team which cache store is already in use for Backstage, since TechDocs caching ties into that configuration. Test page load times from the regions your engineers work in after the first production publish, and record the baseline. The sibling topic on caching TechDocs for faster page loads covers what to cache and how to configure it.
How DeployOpen scopes and delivers TechDocs CI publishing
DeployOpen plans, builds and hands over the CI pipeline that generates and publishes Backstage TechDocs sites with techdocs-cli, inside your own CI system and storage account. Engagements are fixed-scope, with the scope, deliverables and timeline agreed in writing before work starts.
How we scope
- Inventory the docs repositories, catalog entities and CI systems that will generate TechDocs sites
- Record which storage target, bucket layout and publish trigger the written scope covers
- Agree which write tokens exist, who holds them and where they are stored before build begins
- Confirm the Backstage version and techdocs-backend read path that the pipeline must serve
How we build
- Write the CI job that runs techdocs-cli generate against each docs repository in a clean build
- Configure publish with skip-if-unchanged so unchanged sites are not re-uploaded on every commit
- Scope the CI write token to the publish target only, with the backend holding read access alone
- Test a failed publish, a missing token and an unchanged rebuild on a non-production bucket first
How we hand over
- Deliver the pipeline definitions, token scoping notes and bucket policy as versioned files in your repositories
- Document how to rotate CI write tokens and how to check that a site was published or skipped
- Walk your platform and documentation owners through a failed build and a rollback of a bad publish
- Record the agreed deliverables and open decisions in a handover document signed off against the scope
How we support
- Support is available as a retainer for pipeline changes, techdocs-cli upgrades and new docs repositories
- Retainers can cover reviewing CI logs when publishes fail or sites stop updating after a Backstage upgrade
- Ask any provider to confirm response scope, change handling and which versions of techdocs-cli they will maintain
- Ask for the quote to split one-time build effort from recurring retainer effort so costs are visible

