Install DuckHaven¶
DuckHaven runs as one docker compose stack — Postgres, Apache Polaris, and the
DuckHaven API (which serves both the REST API and the web UI on port 8000). No
git clone is required.
Prerequisites¶
- Linux host with Docker Engine 24+ and Docker Compose v2
- 8 GB RAM minimum
- (Recommended) Tailscale or another private network for ingress
Install¶
curl -fsSL https://github.com/tamasmrtn/duckhaven/archive/refs/heads/main.tar.gz \
| tar xz --strip-components=2 duckhaven-main/deploy
docker compose up -d
On first boot the stack generates SECRET_KEY and a one-shot first-admin
setup token, and applies Alembic migrations automatically. No .env editing
required.
Set POSTGRES_PASSWORD before exposing anything
Postgres falls back to the default password duckhaven when
POSTGRES_PASSWORD is unset — a published default, not a generated one.
Set it in the environment before the first boot on any deployment that is
not a private box you trust.
Create the first admin¶
Read the setup token on the host:
Open http://<host>:8000 in a browser. The SPA detects an empty database and
routes you to the setup screen — paste the token, pick admin credentials,
submit. The token is consumed (deleted) after the admin is created and is not
regenerated on subsequent boots.
To start over, wipe the stack:
(This wipes Postgres, secrets, and the setup token.)
Add agents¶
Agents are deployed to separate hosts. See add-agent.md.
Agent network egress¶
The bundled agent is attached to an isolated Docker network with no route off the host. It can reach the API, Polaris, the object store, and the trace collector — and nothing else. Because agents run a broad SQL surface governed by a statement policy rather than a hard allowlist, this is the layer that contains a statement trying to read from or write to an arbitrary address. See Sandboxing.
When you must opt out¶
An agent needs real outbound access when any of these are true:
- a catalog's storage backend is external (
s3,adls_gen2) - Polaris runs off-host
- the OTLP collector runs off-host
Apply the shipped override:
Opting out removes a security layer
With the agent back on the default network, the API statement policy is the only thing left between a session
statement and arbitrary egress. Prefer restricting egress to the hosts you actually need — with a host firewall, or
the NetworkPolicy below — over removing the restriction entirely.
On Kubernetes¶
DuckHaven does not ship Kubernetes manifests yet, so this is a recipe rather than something the product applies for you. A default-deny egress policy for the agent, allowing only DNS plus the hosts it needs:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: duckhaven-agent-egress
spec:
podSelector:
matchLabels:
app: duckhaven-agent
policyTypes:
- Egress
egress:
# DNS resolution.
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# The control plane, Polaris, and the object store.
- to:
- podSelector:
matchLabels:
app: duckhaven-api
- podSelector:
matchLabels:
app: polaris
ports:
- protocol: TCP
port: 8000
- protocol: TCP
port: 8181
# External object storage, if your catalogs use it.
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32 # block the cloud metadata endpoint
ports:
- protocol: TCP
port: 443
NetworkPolicy needs a CNI that implements it
A NetworkPolicy is inert unless your cluster runs a network plugin that enforces it (Calico, Cilium, Antrea,
and others do). Applying this manifest on a cluster without one gives you no protection at all and no error.
Confirm enforcement before relying on it.
Next steps¶
- Update — pull a new release.
- Reverse proxy + TLS — front the stack with Caddy.
- Backup and restore — protecting Postgres.