xip is an authoritative DNS server for dashed-IP hostnames, intended for pullpreview-style ephemeral environments.
Examples:
1-2-3-4-preview.example.test->1.2.3.4preview-1-2-3-4.example.test->1.2.3.4
This project uses mise to manage Go.
mise installmake testmake buildBinary output: dist/xip
make check-fmtmake lintmake testTo auto-run checks before each commit, install pre-commit hooks:
make precommit-installRun them manually:
make precommit-runxip accepts CLI flags and environment variables prefixed with XIP_.
- CLI flags always take precedence over environment variables.
- If a value is not provided via flags, the corresponding
XIP_*env var is used.
| Flag | Env var | Default |
|---|---|---|
--domain | XIP_DOMAIN | xip.test |
--root-addresses | XIP_ROOT_ADDRESSES | 127.0.0.1 |
--ns-addresses | XIP_NS_ADDRESSES | 127.0.0.1 |
--timestamp | XIP_TIMESTAMP | 0 |
--ttl | XIP_TTL | 300 |
--listen-udp | XIP_LISTEN_UDP (or XIP_LISTEN) | :53 |
--listen-tcp | XIP_LISTEN_TCP (or XIP_LISTEN) | :53 |
--listen-http | XIP_LISTEN_HTTP | :80 |
--listen-https | XIP_LISTEN_HTTPS | :443 |
--root-redirect-url | XIP_ROOT_REDIRECT_URL | empty (disabled) |
--blocklist-path | XIP_BLOCKLIST_PATH | /etc/xip/blocklist.csv |
--blocklist-reload-interval | XIP_BLOCKLIST_RELOAD_INTERVAL | 60s |
--acme-cache-dir | XIP_ACME_CACHE_DIR | /etc/xip/acme-cache |
--acme-email | XIP_ACME_EMAIL | empty |
Address lists accept comma or whitespace separators.
The blocklist file is a CSV with 2 columns:
fqdn,reasonabusive.preview.example.com,Malware distribution- FQDN matching is exact and case-insensitive.
- DNS
Arequests for blocked FQDNs are answered with root IP(s). - The file is reloaded from disk every 60 seconds by default.
HTTP server (--listen-http) serves:
GET /health: JSON withblocked_domainsandlast_reload_time.- Blocked hostnames: an inlined error page with the block reason and support contact.
For non-blocked hostnames:
- If
--root-redirect-urlis set, requests are redirected there. - Otherwise the response is
404.
HTTPS server (--listen-https) uses automatic Let's Encrypt certificates via ACME (autocert) with on-disk cache at --acme-cache-dir (default /etc/xip/acme-cache).
Certificate issuance is allowed for any hostname in the configured zone (--domain and all subdomains).
Example:
XIP_ROOT_REDIRECT_URL=https://pullpreview.com/?ref=xipXIP_ACME_CACHE_DIR=/var/lib/xip/acme-cachexip emits:
- OTEL metric
xip_dns_requests_totalfor every DNS request. - OTEL application logs (bridged from
slog).
Metric attributes:
fqdn: queried fully-qualified domain name (normalized)domain: fqdn with the dashed-IP token removed (for exampleip-1-2-3-4.preview.run->preview.run)tld: registrable domain (eTLD+1) derived fromdomainusing the public suffix list (for examplepreview.hello.co.uk->hello.co.uk)
Use standard OTEL env vars, for example:
OTEL_EXPORTER_OTLP_ENDPOINT=ingest.eu.signoz.cloudOTEL_EXPORTER_OTLP_HEADERS=signoz-ingestion-key=<your-ingestion-key>Signal-specific endpoint overrides are also supported:
OTEL_EXPORTER_OTLP_METRICS_ENDPOINTOTEL_EXPORTER_OTLP_LOGS_ENDPOINT
If endpoint env vars omit a scheme, xip defaults them to https://.
XIP_DOMAIN=preview.example.com \XIP_ROOT_ADDRESSES=203.0.113.10 \XIP_NS_ADDRESSES=203.0.113.10 \make run- Create
config/.envfromconfig/.env.exampleand edit values. - Deploy with:
make deployDefault remote target is root@xip.preview.run.
make deploy does the following:
- Builds
xipforlinux/amd64 - SSHes to the target host
- Uploads binary + systemd unit + logrotate policy +
config/.env - Installs binary to
/usr/local/bin/xip - Installs service to
/etc/systemd/system/xip.service - Installs logrotate config to
/etc/logrotate.d/xip - Syncs env file to
/etc/default/xip - Ensures
/etc/xipexists for blocklist + ACME cache - Ensures
/etc/xip/blocklist.csvexists - Ensures
/var/log/xip/xip.logexists - Reloads systemd and restarts
xip.service
Optional deploy overrides:
REMOTE_HOST(defaultxip.preview.run)SSH_USER(defaultroot)SSH_PORT(default22)REMOTE_BIN,REMOTE_SYSTEMD_UNIT,REMOTE_ENV_FILE
Verify deployed service health and print recent logs:
make deploy-checkSync only the blocklist CSV (default target root@xip.preview.run):
make blocklistOptional blocklist sync overrides:
REMOTE_HOST(defaultxip.preview.run)SSH_USER/SSH_PORTLOCAL_BLOCKLIST_FILE
docker build -t xip .docker run --rm -p 53:53/udp -p 53:53/tcp -p 80:80 -p 443:443 --env-file config/.env xip