Local-First (Self-Hosted) Deployment

Last updated 2026-07-15.

Overview

The Local-First subscription runs the entire ET Ducky platform — API server, database, and web dashboard — on infrastructure you own and operate, deployed with a ready-made Docker Compose bundle. Your agents report to your server; telemetry, events, health metrics, and diagnostic data live in your database and never transit ET Ducky's cloud.

It is built for organizations that need data to stay on their own network, teams with large fleets (the subscription includes unlimited agents at one flat price), and MSPs standardizing on their own hosting.

Local-First is not an air-gap product. The instance needs outbound HTTPS for user sign-in, license renewal, and image updates — a short, fixed egress list documented below. If your primary goal is only controlling AI spend or keeping AI prompts on your own provider account, you don't need Local-First: cloud-hosted customers can bring their own AI key.

The Subscription

Details
Price$650/month, or $552.50/month billed annually ($6,630/year — the standard 15% annual discount)
AgentsUnlimited. No per-agent fees, no agent-count tiers, no seat management.
Setup feeNone. An optional guided setup is available for $2,500 (one-time): an ET Ducky engineer takes your team through the entire process below — server prep, deployment, TLS, sign-in verification, and first agent enrollment.
AI queriesUnlimited with BYOK — configure your own Anthropic/OpenAI key and pay your provider directly. Prefer not to manage keys? Add an optional cloud query subscription instead.
Data retentionSelf-managed — limited only by your own disk. No retention add-on applies.
UpdatesPulled from the ET Ducky image registry on your schedule. Version-skipping upgrades are supported.
SupportBusiness-hours email/ticket support with 24-hour response.

How licensing works

Your subscription is embodied in a signed license file (etducky.lic + etducky.lic.sig) downloaded from the Local Deployment page of your etducky.com account. The license covers your entire workspace — every organization in it is served by this one instance, with unlimited agents across all of them. The instance validates the signature at startup and renews the license automatically over HTTPS while your subscription is active. If renewal is interrupted (network outage, expired card), the instance keeps running through a generous grace window and warns administrators in the dashboard well before anything stops. Canceling the subscription stops renewal, but the instance never shuts itself down and never touches your data: past the grace window it drops to collection-only mode — agents keep reporting and telemetry keeps landing in your database, while the dashboard becomes read-only and automations, alerts, AI, and exports pause until the subscription is renewed.

Purchasing, Step by Step

  1. Sign in (or create your account) on etducky.com and make sure you belong to the organization that will own the deployment — the tier is purchased by an organization (only org admins can check out) and licenses your whole workspace — every organization in it runs on the single instance you deploy. If you're brand new, create your account first; your organization and workspace are set up automatically during signup.
  2. Go to the pricing page, open the Self-Hosted tab, and click Purchase Local-First.
  3. Choose billing: monthly ($650/mo, cancel anytime) or annual ($6,630/yr — 15% off).
  4. Complete checkout. Payment is handled by Stripe on etducky.com — your future local instance never touches payment data.
  5. Provisioning is automatic. Within moments of payment: your signed license is issued, registry access is prepared, a confirmation email arrives with your next steps, and a new Local Deployment page appears in your dashboard. You'll be redirected there after checkout.

Your Local Deployment Page

Everything instance-specific lives on one page of your etducky.com account: sign in and go to Configuration → Local Deployment. You'll use it during setup and any time you need to re-download or rotate credentials.

SectionWhat you do there
LicenseDownload etducky.lic and etducky.lic.sig, and see the license status, expiry, and the version your instance last reported.
Renewal secretClick Generate renewal secret. It is displayed exactly once — copy it straight into your .env as ETDUCKY_LICENSE_SECRET. It authorizes your instance's automatic license renewals. Generating a new one immediately replaces the old.
Instance hostnameRegister the exact address your technicians will browse to (e.g. rmm.yourcompany.internal). Required before anyone can sign in — this authorizes your instance's address with the identity service. HTTPS is required. Update it here if the address ever changes.
Registry accessClick Generate pull token for your docker login credentials (also shown once). These are read-only and unique to your organization.

The renewal secret and pull token are shown once by design — we store only hashes. Losing one is a ten-second fix: regenerate it on this page and update your .env or docker login.

Server Requirements

Resource-sufficiency matters. The API server and the bundled database run on the same Docker host and are sized together. An under-resourced host is the most common cause of slow dashboards and delayed agent data — when in doubt, size up, especially disk.

Fleet sizevCPURAMDisk (SSD)
Up to ~250 agents48 GB100 GB
Up to ~1,000 agents816 GB250 GB
Up to ~5,000 agents1632 GB1 TB
Beyond thatContact support for sizing help — larger fleets usually split the database onto its own host.
  • OS: Linux x64 with Docker Engine + Compose v2, or Windows Server 2022+ with Docker Desktop (WSL2 backend). The same containers run on both.
  • Database: bundled — the compose stack runs PostgreSQL with TimescaleDB alongside the API. No separate database install. (If you have a hard requirement for an external database server, contact support: it must have the TimescaleDB extension available, which excludes most managed Postgres offerings.)
  • Disk growth: event and metric history grows with fleet size and retention appetite. Monitor free space; the automatic pre-upgrade backups in ./backups/ also consume disk and are yours to rotate.
  • Network: a DNS name your technicians will browse to, and outbound HTTPS to the egress list below. Agents connect inbound to this hostname on your chosen port.

Before You Begin

Have these ready — the installation itself takes well under an hour:

  • An active Local-First subscription. Everything instance-specific comes from the Local Deployment page of your etducky.com account: the deployment bundle, your license files, and a registry pull token.
  • A Docker host meeting the sizing table above.
  • A DNS name (e.g. rmm.yourcompany.internal) pointing at the host.
  • A TLS reverse proxy (recommended): nginx, Caddy, Traefik, or IIS ARR. The container serves plain HTTP; terminate TLS in front of it.
  • SMTP relay credentials — required for alert and invite email.
  • Your AI choice: an Anthropic/OpenAI API key (BYOK, unlimited queries) or a query subscription on your org.

Installation

  1. Unpack the bundle from your Local Deployment page into a directory, e.g. /opt/etducky.
  2. Create your config: cp .env.template .env and fill in every required value:
    VariableWhat it is
    ETDUCKY_IMAGEImage reference from your Local Deployment page. Pin a version tag for change control, or use stable to follow the latest release.
    ETDUCKY_HOSTNAMEThe hostname your users browse to — must match your DNS/proxy exactly.
    HTTP_PORTHost port the instance serves on (your TLS proxy points here).
    POSTGRES_PASSWORDGenerate once; used only inside the compose network and for backups.
    ETDUCKY_LICENSE_SECRETThe renewal secret from your Local Deployment page (shown once when generated). Authorizes automatic license renewal.
    SMTP_*Your mail relay for alerts and invites.
    APP_DB_PASSWORDOptional. Separate password for the least-privilege database role the application runs as. Leave unset to reuse POSTGRES_PASSWORD.
    ETDUCKY_PUBLIC_ORIGINOptional. Set only when agents reach the instance at an origin other than https://<ETDUCKY_HOSTNAME> — for example a non-standard HTTPS port. Agents discovering the instance are answered with this origin.
    ETDUCKY_SECURITY_NOTIFY_EMAILOptional. Where security-incident notifications are emailed (via your SMTP relay). Leave unset to skip email — incident reports are always written to the storage volume and a review ticket opens in your workspace regardless.
    TokenEncryption__KeyOptional. Encrypts agent registration tokens at rest. Auto-generated on first boot and persisted in the storage volume — set only to bring your own key (openssl rand -base64 32). Note: wiping the storage volume rotates it, which invalidates outstanding registration tokens (they are revocable; simply create new ones).
    ETDUCKY_ORG_IDOptional cross-check. Your numeric organization id. When set, startup fails loudly if the installed license belongs to a different organization — useful when you manage several instances. Leave blank otherwise.
  3. Install your license: download etducky.lic and etducky.lic.sig from the Local Deployment page into ./license/.
  4. Log in to the registry with the pull credentials from the same page. The registry listens on port 8443, and the username contains a $, so quote it:
    docker login registry.etducky.com:8443 -u 'robot$org-<id>'
    Paste the pull token at the password prompt (works the same in bash and PowerShell; pasting at the prompt keeps the secret out of shell history and avoids $-expansion surprises).
    Optional but recommended — verify the image signature with cosign before first start:
    cosign verify --key cosign.pub registry.etducky.com:8443/etducky/etducky-local:stable
    where cosign.pub is ET Ducky's image-signing public key, published here:
    -----BEGIN PUBLIC KEY-----
    MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEF4+SsCeW7XRdBNXrewRE++Qrc40D
    glGZer4TjTYAEqJCSzWkSpDBshiWT9sdbVqnbhmujP4QWij2N9uogEU0QA==
    -----END PUBLIC KEY-----
  5. Start the stack: docker compose up -d
  6. Watch first boot: docker compose logs -f migrate api. Startup always runs in a fixed order — backup → migrate → api — and on a brand-new install the migrate step builds the full database schema (a minute or two). The API then reports healthy. For roughly its first minute the API answers requests with 503 instance_initializing while it completes its first membership sync with etducky.com — this is normal and clears on its own; /health, /ready, and the dashboard shell stay available throughout.
  7. Register your hostname on the Local Deployment page (Instance hostname section) if you haven't already — sign-in at your instance is blocked until its address is authorized. Allow a minute or two for the authorization to propagate before the first sign-in attempt.
  8. Sign in: browse to your hostname and sign in with your existing ET Ducky account (see the next section).
  9. Enroll your first agent: create a registration token in your local dashboard — the one-time reveal shows the exact install command, including the /API_ENDPOINT parameter that points the agent at your instance. Full walkthrough in Enrolling Agents below.

A failed migration never leaves you stranded: the API deliberately does not start, your data is untouched, and the pre-migration database dump is already in ./backups/.

Enrolling Agents

Agents enroll against your hostname using registration tokens created in your local dashboard. The one critical difference from cloud enrollment: the install command carries an extra /API_ENDPOINT parameter that points the agent at your instance instead of ET Ducky's cloud. Your dashboard adds it to the displayed command automatically — just copy the whole command as shown. Requires agent installer 2.4.9.0 or later (the current download always qualifies).

Windows

  1. In your local dashboard, go to Agents → Install New Agent (or the Agent Setup page) and click + New Token. Name it, create it, and keep the reveal dialog open — the token is shown exactly once.
  2. The dialog shows the complete install command for your instance, for example:
    .\ETDuckyAgentSetup.exe /SILENT /SUPPRESSMSGBOXES /NORESTART /REG_TOKEN="etd_..." /API_ENDPOINT="https://rmm.yourcompany.internal"
    The same signed installer is used for every machine — the token and endpoint travel on the command line, so there is nothing machine-specific to download.
  3. Download ETDuckyAgentSetup.exe (link in the dialog) onto the target machine and run the command from an elevated prompt. Or do both in one line:
    curl.exe -fSL -o "%TEMP%\ETDuckyAgentSetup.exe" https://etducky.com/downloads/ETDuckyAgentSetup.exe && "%TEMP%\ETDuckyAgentSetup.exe" /SILENT /SUPPRESSMSGBOXES /NORESTART /REG_TOKEN="etd_..." /API_ENDPOINT="https://rmm.yourcompany.internal"
  4. Verify: sc query ETDuckyAgent shows the service running, and the agent appears in your dashboard within 1–2 minutes. The installer writes a log to %ProgramData%\ETDucky\Agent\logs\installer.log, including the endpoint it configured.

Prefer the wizard? Omit /SILENT and paste the token when prompted — but keep /API_ENDPOINT="..." on the command line either way; it is what points the agent at your instance. An install command without it sends the agent to ET Ducky's cloud, where your token does not exist, and registration fails.

Linux

The curl | bash one-liner shown in the dashboard currently targets ET Ducky cloud workspaces and does not yet accept a custom endpoint — direct one-liner support for Local-First is coming. Until then, Linux enrollment is a short manual step:

  1. Download the package for your distribution from etducky.com/downloads (.deb, .rpm, or the self-extracting .run) and install it (sudo dpkg -i / sudo rpm -i / sudo sh).
  2. Edit /etc/etducky/agent/AgentConfig.json: set "ApiEndpoint" to https://<your-hostname> and "OrganizationKey" to a registration token from your dashboard.
  3. sudo systemctl restart etducky-agent — the agent registers and appears in your dashboard within 1–2 minutes.

Enrollment failing immediately on either OS is almost always certificate trust — see Network Prerequisites below. The agent must reach https://<your-hostname> with a certificate its OS trust store accepts.

Signing In & Managing Your Team

Identity stays with your existing ET Ducky accounts — there is no separate local user database to manage, and removing someone from your workspace’s organizations locks them out of the local instance too.

  • Who can sign in: any member of any organization in your workspace — the license covers the whole workspace, not just the org that bought it. Sign in at your instance's URL with email/password, email code, or Google / Microsoft / Slack — two-factor (TOTP) is fully supported.
  • Browser requirement: the sign-in page in each technician's browser talks directly to ET Ducky's identity service (clerk.etducky.com), so workstations need outbound HTTPS to it. The instance itself uses the same endpoint to validate session tokens.
  • Browser support: Chromium-based browsers (Chrome, Edge, Brave) are fully supported. Safari and Firefox apply third-party-cookie policies that can interfere with sign-in on self-hosted addresses — support for both is being validated; standardize technicians on a Chromium-based browser for now.
  • Inviting technicians: org membership is managed from your etducky.com account (not the local instance — membership changes require our identity backend, which never ships to customer infrastructure). Invite a tech there; once they accept, they sign in at your local URL. The local instance syncs the org roster on a short interval, so removals propagate within minutes and the instance tolerates cloud outages of up to 24 hours without any effect on sign-ins.
  • Managing your subscription: billing, plan changes, and cancellation live in your etducky.com account (Configuration → Subscriptions), not on the local instance — the local dashboard sends you back to etducky.com for anything billing-related.

AI Features: BYOK or Query Subscription

All AI diagnostics (live sessions, multi-agent questions, smart reports, alert enrichment, root-cause analysis) work on Local-First. Choose one of:

  • BYOK (recommended): enter your own Anthropic or OpenAI API key under Settings → AI Settings in your local dashboard. The key is stored encrypted in your database, requests go directly from your instance to your provider, and queries are unlimited — you pay your AI provider, not ET Ducky. See BYOK documentation.
  • Query subscription: if you'd rather not manage provider keys, add any cloud query subscription to your org. Note the disclosed trade-off: AI query content then transits the ET Ducky gateway. This is off by default and entirely optional.

Upgrades

docker compose pull
docker compose up -d
  • Every up re-runs the same backup → migrate → api sequence as install, so a fresh pre-migration database dump lands in ./backups/ before any schema change applies.
  • Version-skipping upgrades (N → N+3) are supported — the migration chain is forward-only and self-applying.
  • Pin a specific image tag in .env to control exactly when upgrades happen; stable follows our latest release. Automate with cron or a scheduled task if desired.

Scheduled updates (opt-in)

Releases after 1.0.13 can install server updates automatically inside a weekly maintenance window you choose in the dashboard (Configuration → Subscriptions → Scheduled updates, workspace admins only). Nothing is enabled by default — without the opt-in, the instance only tells you an update is available.

  1. In .env, uncomment the Scheduled self-update block: COMPOSE_PROFILES=scheduled-updates, ETDUCKY_BUNDLE_DIR (the absolute path of the bundle directory as the Docker daemon sees it, double-quoted), and your registry robot credentials.
  2. docker compose up -d — this adds two containers: the updater and a scoped Docker API proxy.
  3. Open Configuration → Subscriptions, enable the window, and pick a day, time, and timezone (the timezone defaults to your browser's).

During the window the updater runs the same sequence as a manual upgrade — backup → pull → verify → migrate → restart — with two additions: the image signature check (cosign verify) is enforced, and if the new version fails its health check the updater automatically restarts the previous image and reports the rollback in the dashboard. A version that failed once is never retried unattended: review the logs, then re-save the schedule to re-arm. Scheduled updates only follow the stable tag, so pinning a specific version in ETDUCKY_IMAGE pauses them (the dashboard shows why).

The Docker socket is never exposed to the API container. It is reachable only by a proxy (docker-socket-proxy) scoped to the container and image operations an update needs — no exec, no build — on an internal-only network. If exposing the socket to anything is against policy, skip the profile and drive the manual sequence from a host cron or systemd timer instead; leave the dashboard schedule disabled in that case.

Agent updates

Server upgrades (above) and agent updates are separate. Agents enrolled against your instance check for updates on your host — not etducky.com — so agent auto-update works only when you stage the artifacts:

  1. Download version.json plus the installers you need from etducky.com/downloadsETDuckyAgentSetup.exe for Windows fleets, and the .deb/.rpm/.run artifacts if you run Linux agents.
  2. Place them in the downloads/ directory next to docker-compose.yml (it is bind-mounted into the container). Always copy version.json and the installers together, as a matched set.
  3. Agents pick the new version up on their next update check (every 6 hours).

Leaving downloads/ empty pins your fleet: update checks answer "no update" and agents keep their installed version — a legitimate change-control choice. The signed remote-desktop helper is baked into the image and seeded into downloads/rdphelper/ automatically; the mount never shadows it.

Backup & Restore

  • Automatic: a gzip'd pg_dump is written to ./backups/ before every migration.
  • Manual, anytime: docker compose exec db pg_dump -U etducky etducky | gzip > manual-backup.sql.gz
  • Also back up: the storage volume and your ./license/ directory. The storage volume also holds dataprotection-kek.pfx — the key that encrypts your stored secrets (BYOK AI keys, script-repository tokens) at rest, generated automatically on first boot. A database backup restored without this file cannot recover those secrets: the instance runs normally, but you will need to re-enter your AI keys and integration tokens.

Restore (destructive — replaces current data):

docker compose down
docker volume rm <project>_pgdata
docker compose up -d db
gunzip -c backups/<file>.sql.gz | docker compose exec -T db psql -U etducky -d etducky
docker compose up -d

Network Egress (Complete List)

DestinationPurpose
clerk.etducky.comSign-in + session validation (JWKS). Required from both the instance and technicians' browsers.
etducky.comLicense renewal + org membership sync.
registry.etducky.com port 8443Image pulls (installs and upgrades).
Your SMTP relayAlert / invite email.
api.anthropic.com / api.openai.comOnly if you configure BYOK AI keys.
Your configured integrationsOnly if you configure them: alert and automation webhooks, Jira / ServiceNow ticketing, PSA webhooks — HTTP calls go to the endpoints you set.
Software-vendor URLs / api.github.comOnly if you use patch management (vendor version-check URLs from the catalog, winget metadata) or link a GitHub script repository.
etducky.com (IoC relay)Threat-intel blocklists, aggregated by ET Ducky from third-party feeds and relayed to your instance — it never contacts the feed providers directly. Degrades gracefully over a 7-day tolerance if blocked.

Customer telemetry never leaves your infrastructure by default. Two disclosed, optional exceptions — both off by default: the cloud query subscription described above, and minimal health telemetry (ETDUCKY_TELEMETRY_OPT_IN=true — one small daily report: license id, workspace id, instance version, entitlement state, uptime, applied-migration count; nothing else).

TLS

The container serves plain HTTP on HTTP_PORT. Put your reverse proxy in front of it and terminate TLS there — nginx, Caddy, Traefik, or IIS ARR all work. Proxy / and WebSockets to localhost:<HTTP_PORT> (live sessions and remote desktop require WebSocket pass-through).

Network Prerequisites (Your Endpoints)

Every machine that runs an agent — and every technician's browser — needs three things. In a typical shop all three are one-time infrastructure settings, not per-device work:

  1. Name resolution. The instance hostname (the one you registered on your Local Deployment page) must resolve on your network — an internal DNS record or a public one both work. Avoid per-device hosts-file entries; they don't scale and they rot.
  2. Reachability. Endpoints must reach the instance on 443 (or your chosen HTTPS port). That's one inbound allowance on the server / network segment, not a rule on each endpoint.
  3. Certificate trust. Agents and browsers validate the instance's TLS certificate against the operating-system trust store. Use, in order of preference: a public CA certificate (Let's Encrypt supports internal-only hostnames via DNS-01 validation), your enterprise CA (domain-joined machines already trust it via Group Policy), or — last resort — manually installing your CA root on each endpoint.

If agent enrollment fails immediately after install, certificate trust is the usual culprit: run curl.exe https://<your-hostname>/health (no -k) on the endpoint — it must succeed without certificate warnings.

Troubleshooting

  • migrate exited non-zero: the API deliberately did not start. Check docker compose logs migrate. Your data is untouched and the pre-migration backup is in ./backups/.
  • Startup says a license is required or invalid: confirm both etducky.lic and etducky.lic.sig are in ./license/ next to docker-compose.yml, exactly as downloaded (they are signature-checked byte-for-byte — don't edit, re-encode, or let a transfer tool alter them). Re-download both from the Local Deployment page if in doubt.
  • Login works but pages are empty: check that AllowedHosts / ETDUCKY_HOSTNAME matches the hostname you are browsing to.
  • Sign-in button unresponsive or sign-in errors: confirm technicians' browsers can reach clerk.etducky.com, and that your instance hostname was registered on your Local Deployment page (this authorizes the origin for sign-in).
  • Slow dashboard / lagging agent data: check host CPU, RAM, and disk headroom against the sizing table — under-resourcing is the usual cause.
  • Agent install fails with “Configuration failed. Error code: 1” — or the new agent appears in an etducky.com cloud workspace instead of your instance: the install command was missing /API_ENDPOINT="https://<your-hostname>", or the installer predates 2.4.9.0. Re-copy the complete command from a token reveal in your dashboard and download the current installer.
  • docker login says unauthorized: include the port (registry.etducky.com:8443), quote the username ('robot$org-<id>' — the $ gets eaten otherwise), and paste the token at the password prompt. Still stuck? Regenerate the pull token on your Local Deployment page — regeneration replaces the old one immediately.
  • Hostname just registered but sign-in is still blocked: authorization takes a minute or two to propagate. Retry in a fresh tab; if it persists past a few minutes, re-check the registered address matches your URL exactly (scheme, host, and port).
  • Attach a support bundle (releases after 1.0.13): docker compose exec api /app/ETDucky.CloudAPI --support-bundle writes a single zip (recent logs, configuration with secret values redacted, version and entitlement summary, applied migrations) and prints its path; retrieve it with docker compose cp api:<printed path> . If the api container will not start, use docker compose run --rm --entrypoint /app/ETDucky.CloudAPI api --support-bundle. Nothing is sent anywhere — you choose what to attach.
  • Anything else: business-hours support is included; email [email protected]. If you'd like us to drive the whole setup with you, ask about the $2,500 guided setup.