Skip to content

nkgotcode/notailcar

Repository files navigation

NoTailCar

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

Why Nomad-first?

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.

Core architecture

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"]
Loading

Examples

Serve examples

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

Egress examples

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

Repository layout

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

Quickstart

  1. Store the auth key in Nomad Variables:
nomad var put nomad/jobs/notailcar/example/secrets TS_AUTHKEY='<TAILSCALE_AUTH_KEY>'
  1. Validate an example:
nomad job validate examples/egress-worker/egress-worker.nomad.hcl
  1. Plan before running:
nomad job plan examples/egress-worker/egress-worker.nomad.hcl
  1. Run when ready:
nomad job run examples/egress-worker/egress-worker.nomad.hcl

Full walkthrough: docs/quickstart.md.

How the pattern works

  1. A Nomad task group runs with host networking and dynamic local proxy ports.
  2. task "tailscale" starts tailscaled as 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
  3. task "egress-guard" runs as a prestart task:
    • reads TS_AUTHKEY from Nomad Variables
    • waits for the tailscaled socket
    • runs tailscale up
    • optionally selects an exit node and fallback exit nodes
    • optionally verifies observed public egress IP
  4. 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}

Auth key posture

Prefer Tailscale auth keys that are:

  • ephemeral where possible
  • tagged with narrow tags such as tag:nomad-worker or tag: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.

Local validation

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
done

If ShellCheck is installed:

shellcheck scripts/tailscale/*.sh

Documentation

Related projects

Adjacent projects and patterns include Docker-oriented Tailscale sidecars, tsdproxy, Tailscale Kubernetes operator patterns, and small TCP proxy sidecars.

License

MIT. See LICENSE.

About

Tailscale sidecar patterns for private services, workers, exit-node egress, and runtime smoke testing without public ingress with Nomad.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Contributors