CLI

This content was generated with the assistance of AI and should be verified against the official documentation of each tool before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page was written against DocFX 2.81.0 (the latest release, published on 2026-09-25) and checked on 2026-10-26. The option lists below come from docfx --help and docfx <command> --help of the 2.81.0 tool, compared with the official command-line reference and the 2.81.0 source. The commands were run with DocFX installed as a .NET tool in the mcr.microsoft.com/dotnet/sdk:10.0 Docker image (SDK 10.0.403) against the Pocket Units C# library, so the listings show real output. The tool and its commands are introduced in Basic Concepts and Pipeline; this page is the reference for the command line itself.

The shape of the command line

DocFX ships as one executable, docfx, built on the Spectre.Console command-line library. Its general form is:

docfx [COMMAND] [config] [OPTIONS]

config is the path to a docfx.json file. When it is left out, DocFX reads docfx.json in the current folder; a path that does not exist stops the command with Cannot find config file. Relative paths inside docfx.json are resolved against the folder that holds the config file, not against the current folder. Parsing is strict: an unknown option makes the command exit with code 255 without running anything (in the test run it also printed nothing).

docfx --version (or -v) prints the version followed by the commit hash (2.81.0 in this run). docfx --help lists the commands and docfx <command> --help lists the options of one command.

Command What it does Reads config sections

docfx

Without a sub-command, runs the metadata, build and pdf stages in turn; accepts the build options

metadata, build

docfx metadata

Generates YAML files from .NET source code

metadata

docfx build

Generates the static site from the input files

build

docfx pdf

Generates PDF files from an already built site

build

docfx serve

Hosts a folder on a local web server

none

docfx init

Writes an initial docfx.json and sample files

none

docfx template

Lists the embedded templates (list) or copies their files (export)

none

docfx download

Downloads a remote xref map and saves it as a zip archive

none

docfx merge

Merges .NET API YAML files and TOC files

merge

flowchart LR CFG["docfx.json"] --> DEF["docfx
default command"] DEF --> MD["metadata stage"] MD --> BLD["build stage"] BLD --> PDF["pdf stage"] PDF -.->|"--serve"| SRV["local web server"] CFG --> M["docfx metadata"] CFG --> B["docfx build"] CFG --> P["docfx pdf"] B -.->|"--serve"| SRV S["docfx serve folder"] --> SRV

How the commands relate. The bare docfx command runs the three stages in order, the dedicated commands run one stage each, and --serve or docfx serve host the result.

Options shared by several commands

Four logging options are common to docfx, metadata, build, pdf and merge.

Option Meaning

-l, --log

Writes the log as structured JSON, one object per line, to the given file

--logLevel

Threshold: error, warning, info, verbose or diagnostic

--verbose

Shortcut for --logLevel verbose; --logLevel wins when both are given

--warningsAsErrors

Counts warnings as errors, so the command exits with a failure code

The log file holds one JSON object per line with severity, message, date_time and, for messages that belong to a file, file, line and code. Running docfx build --logLevel warning -l build.log.json on a project whose index.md links to a missing page produced one such line for the broken link:

{"severity":"warning","message":"Invalid file link:(~/guide/missing.md).","file":"index.md","line":"3","date_time":"2026-10-26T11:20:01.8017534Z","code":"InvalidFileLink"}

Besides the command line, the rules section of docfx.json can change the severity of individual warning codes; see docfx.json.

Exit codes

A command that logs at least one error ends with exit code 255 (the process shows -1). Warnings alone do not change the code, which is why a broken link passes a build unless --warningsAsErrors is used. The same build, with one broken link and each setting:

$ docfx build
/w/index.md(3,1): warning InvalidFileLink: Invalid file link:(~/guide/missing.md).
...
Build succeeded with warning.

    1 warning(s)
    0 error(s)

$ echo $?
0

$ docfx build --warningsAsErrors
/w/index.md(3,1): warning InvalidFileLink: Invalid file link:(~/guide/missing.md).
...
Build succeeded with warning.

    1 warning(s)
    0 error(s)

$ echo $?
255

The summary line still says "Build succeeded with warning" in the second run; the exit code is what a CI system sees. Exceptions that DocFX does not handle itself, such as a missing config file or a missing Node.js installation, are printed as an error: FatalError: line followed by the exception and a stack trace and also end with 255. The commands serve, template and download do not print the build summary and return 0 when they finish normally.

The bare docfx command

docfx with no sub-command is the everyday entry point. As the source shows, it loads the config and then:

  1. runs the metadata stage when the config has a metadata section;

  2. runs the build stage when the config has a build section, applying the build options below;

  3. runs the pdf stage, which only produces files for tables of contents that opt in with pdf: true;

  4. hosts the output when --serve was given.

So the three forms below differ in what they run. docfx docfx.json and docfx are the same when the file is named docfx.json.

docfx                       # metadata, build and pdf with ./docfx.json
docfx docfx.json --serve    # the same, then host _site at http://localhost:8080
docfx build docfx.json      # build stage only: api/*.yml must already exist

The last form is the one that surprises people: docfx build does not run metadata and does not run pdf. With a fresh checkout where the generated api folder is not committed, docfx build warns that it cannot find toc.yml in api/ and the API section is empty. The bare command, or docfx metadata followed by docfx build, avoids that.

docfx metadata

docfx metadata [config] extracts YAML from C# and Visual Basic projects, solutions, assemblies and source files (the inputs and keys are described in .NET API docs). Besides the shared logging options it accepts the following. They override the same-named keys of the metadata section of docfx.json for every entry of that section.

Option Meaning (from docfx metadata --help)

-o, --output

Output base directory

--outputFormat

Output type (mref, apiPage or markdown)

--filter

Filter config file (the apiRules YAML)

--globalNamespaceId

Name to use for the global namespace

--property

MSBuild properties used when reading project files, written n1=v1;n2=v2 as for MSBuild /property

--disableGitFeatures

Do not fetch Git information; no View Source links are generated

--disableDefaultFilter

Do not apply the default filter, which keeps only public or protected APIs

--noRestore

Do not run dotnet restore before building the projects

--categoryLayout

Category layout in the generated table of contents

--namespaceLayout

Namespace layout in the generated table of contents (flattened or nested)

--memberLayout

Member page layout (samePage or separatePages)

--shouldSkipMarkup

Skip converting the triple-slash comments to Markdown

The help text gives the meaning of each option but not the accepted values of --outputFormat and the layouts; the values in the table are the ones of the matching docfx.json keys. Running it on Pocket Units:

docfx metadata
Loading project /w/src/PocketUnits/PocketUnits.csproj
  Determining projects to restore...
  Restored /w/src/PocketUnits/PocketUnits.csproj (in 789 ms).
Processing PocketUnits
Creating output...


Build succeeded.

    0 warning(s)
    0 error(s)

The api folder then holds PocketUnits.yml, PocketUnits.Converter.yml, PocketUnits.LengthUnit.yml and a generated toc.yml.

docfx build

docfx build [config] renders the site. The options fall into groups.

Output, templates and metadata

Option Meaning

-o, --output

Output base directory. It replaces the output key of the config for this run

-t, --template

Template name(s) to apply, replacing the template key (the run below used -t default,modern). The help says that without a template the YAML files are not transformed

--theme

Theme to use; the help states that default is the theme offered by default

-m, --metadata

A global metadata key and value, --metadata _appTitle="My App"; a key without = becomes true, so --metadata _disableContribution works. The words true and false become booleans. These values are merged into globalMetadata

-x, --xref

URL or path of an xref map used by the content; added to the xref list of the config and de-duplicated

--postProcessors

The order of the post processors, replacing the config value

--markdownEngineProperties

Markdig settings as a JSON string, replacing markdownEngineProperties

--maxParallelism

Maximum parallelism; 0 lets DocFX decide

--disableGitFeatures

Do not fetch Git information for the articles. The default is on, and on a large repository it can slow the build

Running docfx build -o out1 -m _appTitle="Pocket Units" -m _disableContribution -t default,modern wrote the site to out1 and the page title became "Pocket Units | Pocket Units", showing that the metadata value reached the template. The site-wide _appTitle and the other underscore keys are explained in Templates and customization.

Serving the result

Option Meaning

-s, --serve

Host the generated site after the build

-n, --hostname

Host name of the server, for example localhost or *; the default is localhost

-p, --port

Port of the server; the default is 8080

--open-browser

Open the default browser when the server starts

--open-file <RELATIVE_PATH>

Open a specific file of the site instead of the home page

With --serve the command keeps running until it is stopped with Ctrl+C. Before building, DocFX checks whether the port is already taken. When it is, the build is not started:

$ docfx build --serve -p 8099
error: Serve option specified. But TCP port 8099 is already being in use.


Build failed.

    0 warning(s)
    1 error(s)

This ended with exit code 255. The same check runs for the bare docfx --serve. With --hostname '*' any listener on the port counts as a conflict, and a host name that is not a valid IP address is treated the same way. A normal run prints:

Serving "/w/_site" on http://localhost:8101
Press Ctrl+C to shut down

Debugging the templates

These options are for people who write templates or investigate a rendering problem; they are not needed for ordinary builds.

Option Meaning

--dryRun

Do not apply the templates. The documents are processed (links, xrefs, models) but no HTML is written

--exportRawModel

Write the data model that is passed to the template script, as .raw.json files

--rawModelOutputFolder

Folder for the raw model; by default the output folder

--exportViewModel

Write the model after the template script ran, as .view.json files

--viewModelOutputFolder

Folder for the view model; by default the output folder

--debug

Debug mode: the raw and view models are exported automatically when applying a template fails

--debugOutput

Folder for the debug files; by default a docfx folder in the temporary path

The help text names the extensions .raw.model.json and .view.model.json; the files written in the test run were named index.raw.json and toc.raw.json. The help also says that --dryRun is always used together with --exportRawModel or --exportViewModel; the flag can also be given on its own, as in the first run below.

docfx build --dryRun
docfx build --dryRun --exportRawModel --rawModelOutputFolder raw
Dryrun, no template will be applied to the documents.
Building 1 file(s) in ConceptualDocumentProcessor(BuildConceptualDocument=>ValidateConceptualDocumentMetadata)...
Building 3 file(s) in ManagedReferenceDocumentProcessor(...)...
Building 2 file(s) in TocDocumentProcessor(BuildTocDocument)...
Applying templates to 6 model(s)...
XRef map exported.

Build succeeded.

After the first command the _site folder contained only manifest.json and xrefmap.yml: no HTML, no public folder. After the second, raw contained index.raw.json, toc.raw.json and an api folder with one raw file for each generated page. A dry run is therefore a quick way to validate links and cross-references (warnings still appear) and to see the model a custom template would receive; the exported xrefmap.yml also lists every UID. The model formats are described with the templates in Templates and customization.

docfx pdf

docfx pdf [config] accepts only the logging options and -o, --output (output base directory). It does not build the site: it reads manifest.json in the output folder and renders the pages of every table of contents that carries pdf: true in its metadata, using a headless Chromium driven by Playwright. The stage therefore needs a finished docfx build. Two failures seen in the test image:

$ docfx pdf          # before any build
Searching for manifest in /w/_site
error: FatalError: System.IO.DirectoryNotFoundException: Could not find a part of the path '/w/_site/manifest.json'.

$ docfx pdf          # after a build, with "pdf": true in globalMetadata
Searching for manifest in /w/_site
error: FatalError: Docfx.Exceptions.DocfxException: Node.js executable is not found. Try to install Node.js or set the `PLAYWRIGHT_NODEJS_PATH` environment variable.

Both ended with exit code 255. Without pdf: true anywhere, the command finds the manifest, has nothing to render and finishes with "Build succeeded". The Node.js message shows how DocFX works: it looks for a node executable on PATH and sets PLAYWRIGHT_NODEJS_PATH for itself, unless PLAYWRIGHT_NODEJS_PATH or PLAYWRIGHT_DRIVER_SEARCH_PATH is already set. A successful PDF run needs Node.js and the Playwright browsers, which this image lacks; the run that produced PDFs is described on the PDF output page, which also explains the PDF metadata keys and the template requirement are explained in PDF output. Because the bare docfx command runs the pdf stage, a CI job that runs docfx on a config with pdf: true needs Node.js too, while docfx build does not.

docfx serve

docfx serve [directory] hosts a folder of static files and does nothing else; it never builds. The folder defaults to the current directory. Options: -n, --hostname, -p, --port, --open-browser and --open-file <RELATIVE_PATH>, with the same meaning and defaults (localhost, 8080) as for build --serve.

docfx serve _site -p 8099
Serving "/w/_site" on http://localhost:8099
Press Ctrl+C to shut down

A request for / and for /index (no extension) both returned 200; the server maps extension-less URLs to .html files, which makes a local preview behave like hosts that use clean URLs. A folder that does not exist ends with Site folder does not exist. You may need to build it first. and an exception trace. The server is meant for a local preview only: it listens on plain HTTP and has no access control. The official example shows docfx --serve _site for the second case, which is a typographical slip in the reference; the working command is docfx serve _site.

docfx init

docfx init is a short wizard that writes a starter project. Options: -y, --yes (accept all defaults, ask nothing) and -o, --output (folder to write into, default the current folder). The questions are the project name (default mysite), whether to generate .NET API documentation, where the .NET projects are (default src), where the Markdown documents go (default docs), whether to enable site search and whether to enable PDF. Before writing docfx.json it shows the content and asks for confirmation; declining ends the command with "Aborted." and exit code 255. An existing file is only replaced after the same confirmation.

docfx init -y -o /w/newsite
This utility will walk you through creating a docfx project.
It only covers the most common items, and tries to guess sensible defaults.


Project created at /w/newsite

Run docfx /w/newsite/docfx.json --serve to launch the site.

It created docfx.json, toc.yml, index.md, docs/introduction.md, docs/getting-started.md and docs/toc.yml. The generated docfx.json is worth reading, because it shows the idiomatic minimal configuration:

{
  "$schema": "https://raw.githubusercontent.com/dotnet/docfx/main/schemas/docfx.schema.json",
  "metadata": [
    { "src": [ { "src": "../src", "files": [ "**/*.csproj" ] } ], "dest": "api" }
  ],
  "build": {
    "content": [ { "files": [ "**/*.{md,yml}" ], "exclude": [ "_site/**" ] } ],
    "resource": [ { "files": [ "images/**" ] } ],
    "output": "_site",
    "template": [ "default", "modern" ],
    "globalMetadata": { "_appName": "", "_appTitle": "", "_enableSearch": true, "pdf": true }
  }
}

The listing is the generated file with the indentation compacted. Note that the .NET projects are expected one folder above the config file (../src), so run the wizard inside a docs folder next to src, as the overview does, or edit the path. With -y the name is empty, search and PDF are on, and pdf: true in globalMetadata means the bare docfx command will try to build PDFs, so Node.js has to be available there. The $schema line gives editors completion for docfx.json.

docfx template

docfx template list prints the templates embedded in the installation, one per line. docfx template export [template] copies the files of a template so they can be inspected or used as a starting point. Options of export: -a, --all (export every template) and -o, --output (target folder; the default is _exported_templates). With no template name, all templates are exported.

docfx template list
docfx template export modern -o /w/tpl
docfx template export nope
modern
default(zh-cn)
common
statictoc
default
modern is exported to /w/tpl
warning: No resource found for [nope].
warning: Cannot find template nope.

Each template is written to a subfolder named after it (/w/tpl/modern); for modern the export contains ApiPage.html.primary.js and a public folder with the bundled scripts and styles (Mermaid, KaTeX, Lunr and others). An unknown name is only a warning. Exporting is the starting point for the customisation described in Templates and customization.

docfx download

docfx download <path> --xref <url> downloads a remote xref map (a .json or .yml file) and stores it as a zip archive at path. The archive is a zip whatever extension the path has, and its content is converted to xrefmap.yml. The official example downloads the map that DocFX publishes for its own site. The test used a map served from the local site, which keeps the example runnable offline:

docfx serve _site -p 8200 &
docfx download xr.zip --xref http://localhost:8200/xrefmap.yml
/w/xr.zip: Creating xref archive file...
/w/xr.zip: Xref archive file created.

The archive held one file, xrefmap.yml (2593 bytes), starting with ### YamlMime:XRefMap, sorted: true, baseUrl: http://localhost:8200/ and the references of the Pocket Units API. The base URL recorded in the archive is where the links of the other site point. An xref archive cannot itself be a download source: DocFX warns that downloading an archive, or referencing one, is not supported. How to consume an xref map from build.xref is explained in Links and cross-references.

docfx merge

docfx merge [config] is meant to merge the API YAML files and TOC files of several target frameworks into one set. It reads the merge array of docfx.json (each entry has content, dest, globalMetadata, fileMetadata and tocMetadata) and accepts the logging options and -o, --output. The official page carries a warning that the command is broken and does not work as expected, with a link to an issue in the DocFX repository. That matches what was seen in 2.81.0: with no merge section the command ends with a NullReferenceException, and with a minimal valid section it also ends with a NullReferenceException while loading the models:

{ "merge": [ { "content": [ { "files": ["api/*.yml"], "exclude": ["api/toc.yml"] } ], "dest": "merged" } ] }
$ docfx merge merge.json
Start merge metadata...
error: System.NullReferenceException: Object reference not set to an instance of an object.
   at Docfx.Build.Engine.HostServiceCreator.LoadModels(...)

The exit code was 255 and no merged folder was written. Do not build a pipeline on docfx merge. For multi-targeted libraries, choose one target framework with the properties key (see .NET API docs).

Environment variables

DocFX reads a few environment variables. Five are documented in the official reference; the sixth, a Playwright variable, appears in the source and in the error message above.

Variable Effect

DOCFX_SOURCE_BRANCH_NAME

Overrides the Git branch name used for View Source and Improve this Doc links

DOCFX_SOURCE_REPOSITORY_URL

Overrides the organisation and repository, in the form https://host/organization/repository

DOCFX_KEEP_DEBUG_INFO

When true, keeps source-tracking attributes (sourceFile, sourceStartLineNumber, sourceEndLineNumber, jsonPath, data-raw-source, nocheck) in the output HTML

DOCFX_NO_CHECK_CERTIFICATE_REVOCATION_LIST

Disables the certificate revocation check; intended for offline environments

DOCFX_PDF_TIMEOUT

Playwright default timeout, in milliseconds, for PDF generation

PLAYWRIGHT_NODEJS_PATH

Node.js executable used by docfx pdf; found on PATH when not set

DOCFX_SOURCE_BRANCH_NAME and DOCFX_SOURCE_REPOSITORY_URL are useful in CI, where a checkout is often a detached HEAD or a fork. Both are read when the Git information is computed, which is in the metadata stage for API pages, so the variable has to be present when docfx metadata (or the bare docfx) runs, not only at build. In the test the project was a Git repository on branch feature-x with the remote https://github.com/example/pocket-units.git. The View Source link of Converter.cs was:

https://github.com/example/pocket-units/blob/feature-x/src/PocketUnits/Converter.cs/#L12

and after running DOCFX_SOURCE_BRANCH_NAME=docs-main DOCFX_SOURCE_REPOSITORY_URL=https://github.com/other/fork docfx metadata followed by docfx build:

https://github.com/other/fork/blob/docs-main/src/PocketUnits/Converter.cs/#L12

The generated YAML records the values under source, as branch: docs-main and repo: https://github.com/other/fork. Setting the variables only for the build command left the links unchanged. DOCFX_KEEP_DEBUG_INFO was not exercised: the test pages contained none of the listed attributes with or without it, so its effect is taken from the reference. DOCFX_PDF_TIMEOUT and the Node.js variable belong to the PDF stage that was not executed.

Running DocFX from code with Docfx.App

DocFX is also published as the NuGet package Docfx.App ("Docfx published as a library for extensibility and advanced customization" in its description). It exposes a static Docset class with Build(string configPath) and Pdf(string configPath) (each with an overload that takes a BuildOptions value) and, in the namespace Docfx.Dotnet, DotnetApiCatalog.GenerateManagedReferenceYamlFiles(string configPath), which is the metadata stage. BuildOptions has one property, ConfigureMarkdig, a delegate that receives the Markdig MarkdownPipelineBuilder, so a program can add Markdown extensions. DotnetApiOptions can filter symbols with callbacks; it is covered in .NET API docs. The official page warns that the versions of the Microsoft.CodeAnalysis.* packages must match what Docfx.App was built against.

The following console program is the equivalent of docfx metadata followed by docfx build. It was built and run with the .NET 10 SDK against Pocket Units:

BuildSite.csproj
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Docfx.App" Version="2.81.0" />
    <PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="5.9.0" />
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="5.9.0" />
  </ItemGroup>
</Project>
Program.cs
using Docfx;
using Docfx.Dotnet;

// Equivalent of `docfx metadata` followed by `docfx build`.
await DotnetApiCatalog.GenerateManagedReferenceYamlFiles("docfx.json");
await Docset.Build("docfx.json");

The two Roslyn packages are pinned to 5.9.0 because that is the version the 2.81.0 source uses for its net10.0 target (it uses a different one for net8.0 and net9.0). Without them, the program built but failed at run time with The build host could not be found at '.../BuildHost-netcore/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.dll'. Started from the folder that holds docfx.json, it printed the same messages as the command line (Loading project, Processing PocketUnits, Applying templates to 6 model(s), Build succeeded.) and produced the api and _site folders. Note that the program printed Searching built-in plugins in directory .../bin/Debug/net10.0/: the plug-ins and templates are taken from the application’s own output folder.

A complete Pocket Units session

A typical CI sequence, assuming the project layout of the overview page:

dotnet tool install -g docfx --version 2.81.0
export PATH="$PATH:$HOME/.dotnet/tools"

docfx metadata docfx.json --logLevel warning -l metadata.log.json
docfx build docfx.json --warningsAsErrors --logLevel warning -l build.log.json
docfx serve _site --port 8080

The two logs are structured JSON, so a later step can filter by severity. The deployment of the result is the topic of Deployment.

Pitfalls and version notes

  • docfx build alone never regenerates the API YAML and never creates PDFs; use the bare docfx command or run the stages in order.

  • Warnings do not fail a build. Use --warningsAsErrors; the exit code is 255, although the console still says "Build succeeded with warning".

  • --serve refuses to start when the port is taken, before building anything.

  • --dryRun writes manifest.json and xrefmap.yml but no HTML.

  • The bare docfx command runs the pdf stage; with pdf: true in the metadata it needs Node.js on PATH (or PLAYWRIGHT_NODEJS_PATH) and the Playwright browsers.

  • Set DOCFX_SOURCE_BRANCH_NAME and DOCFX_SOURCE_REPOSITORY_URL for the metadata stage, not only for build.

  • docfx merge is documented as broken and failed in 2.81.0.

  • docfx init writes ../src as the projects folder; run it where that path is right.

  • Release 2.80.1 made metadata generation stop when dotnet restore fails; run it with --noRestore only when the projects were restored before.

  • DocFX has no fixed release schedule and is maintained by the community under the .NET Foundation; check docfx --help after an upgrade, because options are not versioned in the reference pages.