No public ingress. Tailnet sidecars for Nomad jobs.
NoTailCar is a Nomad-first toolkit for running private workers and services on a Tailnet without opening them to the public internet. It packages a repeatable pattern: tailscaled as a Nomad sidecar, a prestart guard that authenticates and verifies it, and local HTTP/SOCKS proxy wiring for the workload.
Use it when you want Nomad jobs to:
- join a Tailnet without host-level Tailscale coupling
- expose private services only over Tailnet DNS/Tailscale Serve
- route selected outbound traffic through an exit node
- verify egress IP before a worker starts
- keep Tailscale auth keys in Nomad Variables, not in HCL or git
- run AI agents, dashboards, crawlers, ETL jobs, and private tools without public ingress
Tailscale sidecar already exist for Docker Compose and Kubernetes. Nomad users have fewer polished, replicable patterns for sidecar networking, allocation-local state, lifecycle hooks, Nomad Variables, and batch/service jobs. NoTailCar fills that gap.
flowchart LR
subgraph Nomad["Nomad allocation"]
TS["tailscale task\ntailscaled sidecar"]
Guard["egress-guard prestart\ntailscale up + verify"]
App["worker/service task"]
State[("/alloc/tailscale\nstate + socket")]
Socks["127.0.0.1:NOMAD_PORT_socks"]
HttpProxy["127.0.0.1:NOMAD_PORT_http"]
end
Vars["Nomad Variables\nTS_AUTHKEY"] --> Guard
TS <--> State
Guard --> State
TS --> Socks
TS --> HttpProxy
App --> HttpProxy
App --> Socks
TS --> Tailnet["Tailscale Tailnet"]
Tailnet --> Exit["optional exit node"]
Exit --> Internet["public internet / APIs"]
Private inbound access to Nomad services with Tailscale Serve.
| Example | Type | What it shows | Diagram/docs |
|---|---|---|---|
examples/private-http-service |
service | Private HTTP app configured behind Tailscale Serve; no public ingress | README |
examples/private-dashboard |
service | Private dashboard/admin UI over Tailscale Serve | README |
examples/private-api-service |
service | Private API-style HTTP service with a dedicated service tag | README |
examples/private-multi-port-service |
service | Multiple local service ports published through Serve path routing | README |
examples/private-tcp-service |
service | Raw TCP forwarding through Tailscale Serve | README |
examples/private-serve-config |
service | Rendered Tailscale Services config applied with tailscale serve set-config |
README |
examples/private-service-acl |
service | Private service paired with the sample ACL/tag posture | README |
examples/consul-service-registration |
service | Optional Consul catalog registration for Nomad clusters with Consul integration | README |
Workers, agents, crawlers, exit nodes, and verified outbound IPs.
| Example | Type | What it shows | Diagram/docs |
|---|---|---|---|
examples/egress-worker |
batch | Production-style worker using local HTTP/SOCKS proxies, exit-node var files, optional egress IP verification | README |
examples/agent-worker-egress |
batch | Minimal agent/crawler worker that performs outbound calls through the sidecar | README |
examples/scheduled-batch-egress |
periodic batch | Cron-like Nomad job with Tailnet egress for recurring automation | README |
examples/multi-worker-pool |
batch pool | Multiple workers with per-allocation Tailnet identity | README |
examples/webhook-bridge |
periodic batch | Pull/poll events without exposing a public webhook receiver | README |
examples/
agent-worker-egress/ # minimal outbound worker through Tailnet proxy
egress-worker/ # main hardened sidecar + guard pattern
private-http-service/ # private service behind Tailscale Serve
private-dashboard/ # admin/dashboard UI behind Tailnet access
private-api-service/ # API-style private service behind Tailscale Serve
private-multi-port-service/ # multiple Serve routes to local service ports
private-tcp-service/ # raw TCP Serve forwarding
private-serve-config/ # rendered Tailscale Services config
private-service-acl/ # Serve example paired with ACL posture
consul-service-registration/ # provider=consul service registration
scheduled-batch-egress/ # periodic batch job through Tailnet egress
multi-worker-pool/ # multiple workers with per-alloc Tailnet identities
webhook-bridge/ # polling bridge instead of public webhook ingress
modules/
nomad/ # reusable HCL sidecar snippet
docker/
guard-image/ # optional guard image with debug/verification tools
scripts/
runtime/ # opt-in runtime smoke-test script and env template
tailscale/ # prestart guard scripts
acl/
example-tailscale-acl.json # sample tag-owner posture
docs/
quickstart.md
auth-keys.md
egress-exit-nodes.md
consul.md
nomad-variables.md
production-hardening.md
release-checklist.md
runtime-tests.md
security-model.md
tailscale-acls.md
troubleshooting.md- Store the auth key in Nomad Variables:
nomad var put nomad/jobs/notailcar/example/secrets TS_AUTHKEY='<TAILSCALE_AUTH_KEY>'- Validate an example:
nomad job validate examples/egress-worker/egress-worker.nomad.hcl- Plan before running:
nomad job plan examples/egress-worker/egress-worker.nomad.hcl- Run when ready:
nomad job run examples/egress-worker/egress-worker.nomad.hclFull walkthrough: docs/quickstart.md.
- A Nomad task group runs with host networking and dynamic local proxy ports.
task "tailscale"startstailscaledas a sidecar with userspace networking:--tun=userspace-networking--socks5-server=127.0.0.1:${NOMAD_PORT_socks}--outbound-http-proxy-listen=127.0.0.1:${NOMAD_PORT_http}- state/socket under
/alloc/tailscale
task "egress-guard"runs as a prestart task:- reads
TS_AUTHKEYfrom Nomad Variables - waits for the
tailscaledsocket - runs
tailscale up - optionally selects an exit node and fallback exit nodes
- optionally verifies observed public egress IP
- reads
- The workload receives proxy env vars:
HTTP_PROXY=http://127.0.0.1:${NOMAD_PORT_http}HTTPS_PROXY=http://127.0.0.1:${NOMAD_PORT_http}ALL_PROXY=socks5://127.0.0.1:${NOMAD_PORT_socks}
Prefer Tailscale auth keys that are:
- ephemeral where possible
- tagged with narrow tags such as
tag:nomad-workerortag:private-service - preauthorized only when automation requires it
- stored in Nomad Variables or another secret store
- rotated when changing tag policy or after suspected exposure
Never commit real auth keys, Tailnet hostnames, private IP inventories, production ACLs, customer configs, or internal workload names.
sh -n scripts/tailscale/*.sh
nomad fmt -check -recursive .
for f in examples/*/*.nomad.hcl; do
nomad job validate "$f"
done
for vf in examples/egress-worker/vars/*.hcl; do
nomad job validate -var-file="$vf" examples/egress-worker/egress-worker.nomad.hcl
doneIf ShellCheck is installed:
shellcheck scripts/tailscale/*.sh- Quickstart
- Tailscale auth keys
- Tailscale ACLs and tags
- Nomad Variables
- Egress and exit nodes
- Consul integration
- Choosing an exit node
- Security model
- Production hardening
- Runtime tests
- Troubleshooting
- Release checklist
- Optional guard image
Adjacent projects and patterns include Docker-oriented Tailscale sidecars, tsdproxy, Tailscale Kubernetes operator patterns, and small TCP proxy sidecars.
MIT. See LICENSE.