DNSControl 5.7 常用 CLI 命令详解

DNSControl v5.7.0 全子命令实测:preview/push(--expect-no-changes、--report、--full、-i)、check、print-ir、get-zones 五种导出格式、check-creds、fmt、write-types、shell-completion 与废弃命令说明,回显逐行照抄。

DNSControlDNSControlDNS服务器网络自动化运维2026/10/26

DNSControl 5.7 常用 CLI 命令详解

目标:逐个讲清 DNSControl v5.7.0 全部子命令的用途、参数与真实回显——main 组(init/preview/push)、debug 组(check/print-ir/version)、utility 组(get-zones/check-creds/fmt/write-types/shell-completion/create-domains)及全局参数。全部命令已在 Ubuntu 22.04 + DNSControl v5.7.0 + BIND 9.18.39 环境实测(2026-10-25),回显逐行照抄。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS,x86_64
  • 软件版本:DNSControl v5.7.0;配套权威 DNS:BIND 9.18.39-0ubuntu0.22.04.7
  • 工作目录 /etc/dnscontrol/:dnsconfig.js(管理 example.io,NS 为 ns1.example.io.,www A 记录 192.168.1.72)与 creds.json(BIND provider,directory /srv/zones/primary),均已在 BIND9 上跑通
  • 安装与首次配置见 《Ubuntu 22.04 安装 DNSControl 5.7 指南》;DSL 参数详解见 《DNSControl 5.7 配置参数详解》

二、工具概览

dnscontrol --help 实测输出(子命令全景):

NAME:
   dnscontrol - DNSControl is a compiler and DSL for managing dns zones

USAGE:
   dnscontrol [global options] [command [command options]]

VERSION:
   DNSControl version v5.7.0

COMMANDS:
   help, h  Shows a list of commands or help for one command

   main:
     init     Interactively create a creds.json and starter dnsconfig.js
     preview  read live configuration and identify changes to be made, without applying them
     push     identify changes to be made, and perform them

   debug:
     check     Check and validate dnsconfig.js. Output to stdout.  Do not access providers.
     print-ir  Output intermediate representation (IR) after running validation and normalization logic.
     version   Print version information

   utility:
     check-creds          Do a small operation to verify credentials (stand-alone)
     create-domains       DEPRECATED: Ensures that all domains in your configuration are activated at their Domain Service Provider (This does not purchase the domain or otherwise interact with Registrars.)
     fmt                  [BETA] Format and prettify a given file
     get-zones, get-zone  gets a zone from a provider (stand-alone)
     shell-completion     generate shell completion scripts
     write-types          [BETA] Write a TypeScript declaration file in the current directory

GLOBAL OPTIONS:
   --debug            Enable debug logging
   --allow-fetch      Enable JS fetch(), dangerous on untrusted code!
   --disableordering  Disables update reordering
   --no-colors        Disable colors
   --help, -h         show help
   --version, -V      print the version

分组解读:日常只有 preview/push 两个动词;check/print-ir/version 用于排障与调试;utility 组是围绕 provider 的独立小工具(不读 dnsconfig.js 也能用)。全局参数中排障最常用 --debug,CI 环境建议加 --no-colors。

三、前置条件

cd /etc/dnscontrol   # 以下命令默认读当前目录的 dnsconfig.js / creds.json
dnscontrol version
v5.7.0

四、main 组命令

4.1 version / -V:版本确认

dnscontrol -V
dnscontrol version

实测回显(两行分别对应两个写法,-V 带前缀):

dnscontrol version DNSControl version v5.7.0
v5.7.0

4.2 init:交互式生成初始配置

dnscontrol init --help 实测:

NAME:
   dnscontrol init - Interactively create a creds.json and starter dnsconfig.js

USAGE:
   dnscontrol init [options]

CATEGORY:
   main

DESCRIPTION:
   Walks you through picking a registrar and DNS provider, entering their credentials, and writing a creds.json plus a minimal dnsconfig.js so a fresh setup can run `dnscontrol preview` immediately.

OPTIONS:
   --creds string   Output path for the credentials file (default: "creds.json")
   --config string  Output path for the starter DNSControl config (default: "dnsconfig.js")
   --no-config      Do not write a starter dnsconfig.js
   --help, -h       show help

逐选项说明:--creds/--config 改输出路径;--no-config 只生成凭证不生成配置。init 为交互式向导(逐项提问),适合首次接入云厂商 provider;自建 BIND 场景手工写两个文件更直接(见安装篇第六节)。

4.3 preview:比对差异、不落地

核心参数(preview --help 实测节选):

   --config string       File containing dns config in javascript DSL (default: "dnsconfig.js")
   --creds string        Provider credentials JSON file (or !program to execute program that outputs json) (default: "creds.json")
   --providers string    Providers to enable (comma separated list); default is all.
   --domains string      Comma separated list of domain names to include
   --notify              set to true to send notifications to configured destinations
   --expect-no-changes   set to true for non-zero return code if there are changes
   --cmode string        Which providers to run concurrently: concurrent, none, all (default: "concurrent")
   --cmax int            Maximum number of concurrent connections (default: 100)
   --no-populate         Do not auto-create zones at the provider
   --populate-on-preview Auto-create zones on preview
   --full                Add headings, providers names, notifications of no changes, etc
   --bindserial int      Force BIND serial numbers to this value (for reproducibility) (default: 0)
   --report string       Generate a machine-parseable report of corrections.

(1) 无变更基准(配置与线上一致时):

dnscontrol preview
CONCURRENTLY checking for 1 zone(s)
Waiting for concurrent checking(s) to complete...DONE
CONCURRENTLY gathering records of 1 zone(s)
Waiting for concurrent gathering(s) to complete...DONE
******************** Domain: example.io
Done. 0 corrections.

(2) --expect-no-changes(CI 漂移检测):无变更时退出码 0;人为改动配置后出现待变更时退出码 1:

dnscontrol preview --expect-no-changes; echo "rc=$?"

无变更实测:

...
Done. 0 corrections.
rc=0

把 www 的 A 记录从 192.168.1.72 改成 192.168.1.73 后再跑:

******************** Domain: example.io
1 correction (bind)
#1: ± MODIFY www.example.io A (192.168.1.72 ttl=300) -> (192.168.1.73 ttl=300)
Done. 1 corrections.
there are pending changes
rc=1

(3) --report:机器可解析报告:

dnscontrol preview --report=/tmp/report.json
cat /tmp/report.json

实测回显:

[
  {
    "domain": "example.io",
    "corrections": 1,
    "correction_details": [
      "± MODIFY www.example.io A (192.168.1.72 ttl=300) -> (192.168.1.73 ttl=300)"
    ],
    "provider": "bind"
  },
  {
    "domain": "example.io",
    "corrections": 0,
    "registrar": "none"
  }
]

(4) --domains / --providers 过滤范围(多域多 provider 时缩小执行面):

dnscontrol preview --domains example.io
dnscontrol preview --providers bind

两者实测均正常完成比对并输出 Done. 0 corrections.。

(5) --full:打印各 provider 分组汇总与统计(含零变更项,便于审计留档):

----- DNS Provider: bind...
0 corrections (bind)
----- Registrar: none...
0 corrections (none)
Inaccurate statistics: {"memory_in_use":18155800,"memory_in_use_str":"18 MB","num_records":12,"num_zones":1,"rc_size":200,"benchmark1":1512983,"benchmark1str":"1.4 MiB bytes"}
Done. 0 corrections.

(6) --debug:看三段式执行过程(preview --debug 实测节选):

Reading dnsconfig.js or equiv.
Reading creds: "creds.json"
Creating an in-memory model of 'desired'...
Normalizing and validating 'desired'..
PHASE 1: CHECKING for missing zones
...
PHASE 2: GATHERING data
...
PHASE 3: CORRECTIONS

4.4 push:应用变更

参数与 preview 相同,另多一个 -i(Interactive,逐条确认/排除变更后再执行)。

(1) 首次 push(zone 不存在时自动建 zone,再写入全部记录):

dnscontrol push

实测回显(节选首尾):

******************** Domain: example.com
1 correction (bind)
#1: Ensuring zone "example.com" exists in "bind"
SUCCESS!
...
#1: + CREATE example.com SOA ns1.example.com. hostmaster.example.com. X 3600 600 604800 3600 ttl=300
+ CREATE example.com A 192.168.1.31 ttl=300
...
+ CREATE blog.example.com CNAME www.example.com. ttl=300
WRITING ZONEFILE: "/srv/zones/primary/example.com.zone" (serial 2026102500)
SUCCESS!
Done. 14 corrections.

(2) 增量 push(只应用差异;serial 自动 +1):

#1: ± MODIFY www.example.com A (192.168.1.31 ttl=300) -> (192.168.1.32 ttl=300)
WRITING ZONEFILE: "/srv/zones/primary/example.com.zone" (serial 2026102501)
SUCCESS!
Done. 1 corrections.

⚠️ 注意:BIND provider 下 push 只写 zone 文件,要让 BIND9 生效需再执行 rndc reload(实测:reload 前 dig 返回旧值 192.168.1.32,reload 后为新值 192.168.1.33)。--no-populate 可禁止 push 自动建 zone(报 zone 不存在而非自动创建)。

五、debug 组命令

5.1 check:离线校验

dnscontrol check
No errors.

只解析+校验 dnsconfig.js,不访问任何 provider,适合作为提交前钩子。支持 --config、--variable、-v。语法错误实测:

executing bad.js: (anonymous): Line 4:2 Unexpected token ;

(退出码 1,行号定位到出错位置。)

5.2 print-ir:查看中间表示

dnscontrol print-ir --pretty | head -20

实测回显(节选):

{
  "registrars": [
    {
      "name": "none",
      "type": "-"
    }
  ],
  "dns_providers": [
    {
      "name": "bind",
      "type": "-",
      "meta": {
        "default_soa": {
          "expire": 604800,
          "master": "ns1.example.io.",
          "mbox": "hostmaster.example.io.",
          "minttl": 300,
          "refresh": 3600,
          "retry": 600
        }
      }
    }
  ],

参数(print-ir --help 实测):--pretty 格式化;--raw 跳过校验与规范化直接输出 JS 执行结果;--out 写文件;--ir 直接读 IR JSON 而不跑 DSL;--variable/-v 注入变量。用途:确认 DSL 渲染结果、排查 TTL/修饰符是否生效。

六、utility 组命令

6.1 get-zones:从 provider 反向导出 zone

不依赖 dnsconfig.js,直接按 creds.json 里的实例名拉取。格式参数 --format:js / djs / zone / tsv / nameonly(默认 zone)。

(1) zone 格式(BIND zone file 文本):

dnscontrol get-zones --format=zone bind example.com

实测回显:

$ORIGIN example.com.
$TTL 300
@                IN SOA   ns1.example.com. hostmaster.example.com. 2026102501 3600 600 604800 3600
                 IN NS    ns1.example.com.
                 IN A     192.168.1.31
                 IN MX    10 mail.example.com.
                 IN TXT   "v=spf1 ip4:192.168.1.31 -all"
                 IN CAA   0 issue "letsencrypt.org"
_dmarc           IN TXT   "v=DMARC1; p=none; rua=mailto:dmarc@example.com"
_sip._tcp        IN SRV   10 60 5060 sip.example.com.
blog             IN CNAME www.example.com.
mail             IN A     192.168.1.31
ns1              IN A     192.168.1.31
www              IN A     192.168.1.32
                 IN AAAA  2001:db8::31

(2) tsv 格式(便于脚本处理,节选 5 行):

example.com	@	300	IN	SOA	ns1.example.com. hostmaster.example.com. 2026102501 3600 600 604800 3600
example.com	@	300	IN	NS	ns1.example.com.
example.com	@	300	IN	A	192.168.1.31
example.com	@	300	IN	MX	10 mail.example.com.
example.com	@	300	IN	TXT	v=spf1 ip4:192.168.1.31 -all

(3) nameonly 与 all(列出该 provider 全部 zone 名):

dnscontrol get-zones --format=nameonly bind all
example.com

(4) js 格式:生成 dnsconfig.js 草稿(存量域名纳入管理的起点):

dnscontrol get-zones --format=js bind example.com

实测回显:

// generated by get-zones. This is 'a decent first draft' and requires editing.

var DSP_BIND = NewDnsProvider("bind", "");
var REG_CHANGEME = NewRegistrar("none");

D("example.com", REG_CHANGEME,
	{no_ns: "true"},
	DnsProvider(DSP_BIND),
	//SOA("@", "ns1.example.com.", "hostmaster.example.com.", 3600, 600, 604800, 3600),
	//NAMESERVER("ns1.example.com."),
	A("@", "192.168.1.31"),
	MX("@", 10, "mail.example.com."),
	TXT("@", "v=spf1 ip4:192.168.1.31 -all"),
	CAA("@", "issue", "letsencrypt.org"),
	TXT("_dmarc", "v=DMARC1; p=none; rua=mailto:dmarc@example.com"),
	SRV("_sip._tcp", 10, 60, 5060, "sip.example.com."),
	CNAME("blog", "www.example.com."),
	A("mail", "192.168.1.31"),
	A("ns1", "192.168.1.31"),
	A("www", "192.168.1.32"),
	AAAA("www", "2001:db8::31"),
);

(5) djs 格式 + --out 落盘:

dnscontrol get-zones --format=djs --out=draft.js bind example.com

生成的 draft.js 与 js 格式内容一致,仅逗号前置风格不同(djs 方言)。另有 --ttl int 参数:导出时给无 TTL 记录指定默认 TTL,0(默认)表示取 zone 内最常见 TTL。

6.2 check-creds:凭证连通性自检

dnscontrol check-creds bind

实测回显(成功时列出该 provider 可见的 zone 清单):

example.com

退出码 0。也兼容旧式两参数写法 dnscontrol check-creds bind BIND(第二参数为 provider 类型)。失败案例实测(creds 条目缺 TYPE 字段):

failed GetZone CDP: creds.json entry missing TYPE field

退出码 1。--creds 指定的文件不存在时:

INFO: Config file "/etc/dnscontrol/nope.json" does not exist. Skipping.
failed GetZone LZ: directory "zones" does not exist

6.3 fmt [BETA]:格式化 dnsconfig.js

dnscontrol fmt --input=/tmp/messy.js --output=/tmp/messy_fmt.js --verbose

实测回显:

/tmp/messy_fmt.js (formatted)

--verbose 打印处理结果;diff 前后文件可见缩进被统一(实测差异节选):

<   "default_soa": {
---
>     "default_soa": {

对已规范文件再跑 fmt 幂等(diff 无实质差异)。默认输入输出都是当前目录 dnsconfig.js(原地格式化),生产使用前建议先 --output 到临时文件比对。

6.4 write-types [BETA]:生成 TypeScript 类型声明

dnscontrol write-types

实测回显:

Successfully wrote types-dnscontrol.d.ts
-rw-r--r-- 1 root root 205187 Oct 25 14:04 types-dnscontrol.d.ts

生成的 types-dnscontrol.d.ts(约 200KB)导入 VS Code 等 IDE 后,编辑 dnsconfig.js 可获得 DSL 函数的自动补全与签名提示;--dts-file/-o 改输出路径。

6.5 shell-completion:生成 shell 补全脚本

dnscontrol shell-completion bash | head -6

实测回显(节选):

#!/bin/bash

: "dnscontrol"

# Macs have bash3 for which the bash-completion package doesn't include
# _init_completion. This is a minimal version of that function.

支持 bash | zsh,把完整输出重定向到 /etc/bash_completion.d/dnscontrol 即可启用 Tab 补全。

6.6 create-domains:已废弃

v5.7.0 实测输出:

DEPRECATED: This command is deprecated. The domain is automatically created at the Domain Service Provider during the push command.
DEPRECATED: To prevent disable auto-creating, use --no-populate with the push command.
***  example.io
  - bind

zone 的自动创建已并入 push,该命令仅为兼容保留,新流程不要再用。

七、注意事项

  1. 退出码约定:preview/push 正常完成为 0;--expect-no-changes 检测到待变更时为 1;语法错误/凭证失败为 1。但 push 中个别 provider 写文件失败(如目录不存在)会输出 completed with errors 而退出码仍为 0,自动化脚本须同时检查输出文本。
  2. 并发参数:--cmode concurrent(默认)并发检查/拉取多 zone,--cmax 100 限制最大并发连接;zone 数量大或 provider 限流时调低。
  3. 颜色输出:TTY 下 corrections 带 ANSI 颜色,写日志/CI 加全局 --no-colors。
  4. 生效链路:BIND provider 的 push 终点是 zone 文件,BIND9 需 rndc reload 后才对外生效;云厂商 provider 的 push 直接调 API 即时生效。
  5. serial 生成:BIND zone 的 SOA serial 自动取 YYYYMMDDHH 日期序号,同日内多次 push 递增;需要可复现构建时用 --bindserial 钉死。

八、常见问题

8.1 子命令拼错

Unknown command: nosuchcmd

退出码 1。用 dnscontrol help 或 dnscontrol <子命令> --help 查正确拼写。

8.2 preview 正常但 push 报 could not create zonefile

creds.json 里 BIND provider 的 directory 不存在。mkdir -p 后重试(详见安装篇常见问题 9.2 的完整排障过程)。

8.3 check 报 ReferenceError

配置引用了未注入的变量,补 --variable 名=值(见配置参数详解第八节)。

8.4 想让 preview 在 CI 里有变更即失败

加 --expect-no-changes,并用 --report=report.json 留机器可解析证据(4.3 节实测)。

九、参考链接