# Set up a hybrid on-prem execution host

Connect a dedicated Linux server to the hosted Hodman workspace. This guide is for the administrator configuring Docker, network access and agent credentials.

## 1. Understand the deployment boundary

Hodman hosts the web UI and API, including workspace administration, tasks and conversations. The execution agent runs on your host and opens an authenticated outbound WebSocket connection to wss://hodman.ai/ws-agent over TCP 443. Tasks and results travel in both directions over that established connection. There is no inbound agent management port to expose.

The agent controls Docker to run project containers. Project files and provisioned databases are stored on your host. Internal databases and APIs need to be reachable from the project containers, not from the public internet.

Local storage is not a no-egress guarantee. Requested file contents, query results, command output and logs can return to Hodman and enter AI context. The selected AI provider can receive that context; inference is not made local by this setup. Use scoped credentials and verified connector-side filtering. This is neither an air-gapped deployment nor a fully self-hosted Hodman control plane.

## 2. Prepare the host

Use a dedicated Linux host or VM with Docker Engine and the Docker Compose plugin. Confirm that the release supports your host architecture. A starting estimate from the deployment notes is 4 CPU cores, 16 GB RAM and 200 GB SSD for roughly four modest projects; builds and real workloads may require more. This is a sizing estimate, not a capacity guarantee.

You need an eligible Hodman workspace, administrator access to its Agents section, and a dedicated Unix account with write access to project directories. Membership in the Docker socket group gives powerful host-level access. Do not share this host with unrelated sensitive workloads.

The baseline below runs PostgreSQL locally and does not publish its port. The bootstrap database account provisions project databases and roles; treat it as a privileged infrastructure credential. Do not reuse it as an application's database account. Qdrant appeared in the older deployment archive but is not required by the current minimal agent configuration reviewed for this guide.

Host checks — run on the Linux execution host

```
docker --version
docker compose version
uname -m
stat -c '%g' /var/run/docker.sock
```

## 3. Plan management, application access and egress separately

Management: allow the agent to resolve hodman.ai and open WSS to hodman.ai:443. An outbound proxy must support long-lived WebSocket upgrades. Never publish the Docker daemon, PostgreSQL or an agent management port to the internet.

Image and dependency downloads: allow the registry authentication and content endpoints required by Docker Hub, any additional project-image registries, and the package repositories used by your builds. Approve external integrations and AI destinations according to the actual workflow; the sample is not a complete egress allowlist.

Application access: decide separately who should open previews, published applications and webhook endpoints. Private DNS, internal HTTPS ingress and a VPN can keep application access inside your network. A browser opening an internal preview must have network access to it. The WSS management connection does not by itself publish an application or make an internal preview internet-accessible.

Default project hostnames use project-slug.project-host.base-host, for example preview-demo.runner-1.apps.example.com. Set PROJECT_HOST and DZUN_AI__ROUTING_BASE_HOST consistently, configure the corresponding DNS records and route them to your ingress. Multiple hosts can have different PROJECT_HOST values.

Traefik is one ingress option, not a requirement for agent registration. The sample deliberately omits ingress and publishes no ports. If you add Traefik, attach it to dzun_ai_edge, expose only intended routes, and align its entrypoint and certificate-resolver names with the agent routing variables. For internal-only applications use a suitable internal certificate authority or an approved DNS-01 setup. Let's Encrypt HTTP-01 requires inbound port 80; it is not compatible with a completely private endpoint without additional design. Do not add public 80/443 rules just to make the agent connect.

## 4. Register the execution agent and protect its key

As workspace owner, open Agents in the Hodman console and issue an agent key. Save its UUID and private key. Choose an agent ID and project host for this machine. Keys must belong to your workspace; never copy another customer's credentials or commit a private key to Git.

Save the private key as ./secrets/agent-private.pem. The configuration uses AGENT_PRIVATE_KEY_PATH, which the agent supports, rather than embedding a multiline private key in .env. Restrict the file to the dedicated agent UID and mount it read-only.

Inside your deployment directory

```
mkdir -p secrets
chmod 700 secrets
# Save the private key issued in the Hodman console here.
chmod 600 secrets/agent-private.pem
```

## 5. Create .env and persistent directories

Replace every CHANGE_ME value below. Use the numeric UID and GID of the dedicated agent account; AGENT_DOCKER_GID must match the Linux Docker socket's group ID. Generate a strong unique PostgreSQL password. Use absolute host paths.

AGENT_IMAGE is intentionally a release placeholder. The anticipated repository name is an assumption for the public release, not proof of availability. Pin a confirmed immutable release tag or digest. Public image availability does not establish an open-source licence. The project runtime is a different image. Pin its confirmed release tag separately; the example deliberately does not guess a compatible version.

A quoted password avoids Compose interpolation of dollar signs. Protect .env, do not add it to source control, and never paste docker compose config output into a support ticket without removing secrets.

.env — replace placeholders before running Compose

```
AGENT_IMAGE=hodmanai/hodman-ai-agent:CHANGE_ME_RELEASE_TAG
RUNTIME_IMAGE=hodmanai/hodman-ai-runtime:CHANGE_ME_RELEASE_TAG
DATA_DIR=/srv/hodman
AGENT_UID=CHANGE_ME_NUMERIC_UID
AGENT_GID=CHANGE_ME_NUMERIC_GID
AGENT_DOCKER_GID=CHANGE_ME_DOCKER_SOCKET_GID
AGENT_ID=runner-1
PROJECT_HOST=runner-1
BASE_HOST=apps.example.com
AGENT_KEY_UUID=CHANGE_ME_KEY_UUID
POSTGRES_PASSWORD='CHANGE_ME_STRONG_PASSWORD'
```

## 6. Set filesystem ownership

Run these commands on the dedicated Linux host after replacing the numeric-ID placeholders. The private key must be readable by the same UID that runs the agent. The official PostgreSQL container manages its own data-directory ownership; do not assign that directory to the agent account.

The PostgreSQL 18 example mounts /var/lib/postgresql, with its version-specific data directory managed by the image. Do not mount an existing older-major-version PGDATA directory into it; use a planned PostgreSQL migration for existing databases.

Host directories and key permissions

```
sudo mkdir -p /srv/hodman/workdir /srv/hodman/apps-data /srv/hodman/postgres
sudo chown CHANGE_ME_UID:CHANGE_ME_GID /srv/hodman/workdir /srv/hodman/apps-data
sudo chown CHANGE_ME_UID:CHANGE_ME_GID secrets/agent-private.pem
chmod 600 .env
sudo chmod 600 secrets/agent-private.pem
```

## 7. Create compose.yaml

This is a minimal example for a new dedicated host. It starts the execution agent and local PostgreSQL, creates the two Docker networks expected by project containers, and leaves all host ports unpublished. It does not configure ingress, VPN, backups or your company firewall.

Inside the agent container, file operations use /workdir and /data. Docker creates sibling project containers using host paths instead, so AGENT_HOST_WORKDIR_BASE and AGENT_HOST_DATA_BASE must point to the matching absolute host directories. Mixing these two path spaces can create empty or incorrect mounts.

The Docker socket mount lets the agent create and control containers. A read-only bind of the socket would not make Docker API calls read-only. Protect the host and the agent key accordingly. Template prewarming is disabled in this baseline to avoid unnecessary bootstrap work; configure it separately if needed.

compose.yaml

```
services:
  postgres:
    image: postgres:18
    restart: unless-stopped
    environment:
      POSTGRES_USER: hodman_admin
      POSTGRES_DB: hodman_agent
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Set POSTGRES_PASSWORD}
    volumes:
      - ${DATA_DIR:?Set DATA_DIR}/postgres:/var/lib/postgresql
    healthcheck:
      test: [CMD-SHELL, 'pg_isready -U hodman_admin -d hodman_agent']
      interval: 10s
      timeout: 5s
      retries: 12
    networks: [core]

  agent:
    image: ${AGENT_IMAGE:?Set a confirmed AGENT_IMAGE}
    restart: unless-stopped
    user: '${AGENT_UID:?Set AGENT_UID}:${AGENT_GID:?Set AGENT_GID}'
    group_add:
      - '${AGENT_DOCKER_GID:?Set AGENT_DOCKER_GID}'
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      API_WS_URL: wss://hodman.ai/ws-agent
      AGENT_ID: ${AGENT_ID:?Set AGENT_ID}
      PROJECT_HOST: ${PROJECT_HOST:?Set PROJECT_HOST}
      AGENT_KEY_UUID: ${AGENT_KEY_UUID:?Set AGENT_KEY_UUID}
      AGENT_PRIVATE_KEY_PATH: /run/secrets/agent-private.pem
      AGENT_WORKDIR_BASE: /workdir
      AGENT_HOST_WORKDIR_BASE: ${DATA_DIR}/workdir
      AGENT_DATA_BASE: /data
      AGENT_HOST_DATA_BASE: ${DATA_DIR}/apps-data
      AGENT_PG__HOST: postgres
      AGENT_PG__PORT: '5432'
      AGENT_PG__DATABASE: hodman_agent
      AGENT_PG__USERNAME: hodman_admin
      AGENT_PG__PASSWORD: ${POSTGRES_PASSWORD}
      AGENT_PROJECT_PG__HOST: postgres
      AGENT_PROJECT_PG__PORT: '5432'
      AGENT__MIGRATIONS: /app/harness/apps/runner/drizzle
      AGENT__TEMPLATE_POOL_ENABLED: 'false'
      AGENT__TEMPLATE_POOL_DEFAULT_DOCKER_IMAGE: ${RUNTIME_IMAGE:?Set RUNTIME_IMAGE}
      DZUN_AI__ROUTING_BASE_HOST: ${BASE_HOST:?Set BASE_HOST}
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ${DATA_DIR}/workdir:/workdir
      - ${DATA_DIR}/apps-data:/data
      - ./secrets/agent-private.pem:/run/secrets/agent-private.pem:ro
    networks: [core, edge]

networks:
  core:
    name: dzun_ai_network
  edge:
    name: dzun_ai_edge
```

## 8. Pull the confirmed release and start

Check that all placeholders are replaced. Compose config validation checks structure and required values; it does not prove registry availability, permissions or connectivity. Do not run source .env: a Compose environment file is not a shell script.

For the planned public distribution, installation does not require credentials for Hodman's private registry. Docker Hub authentication may still be used under your organization's policy or for pull limits. If the anticipated agent image is unavailable, confirm publication and its exact reference rather than trying private credentials or guessing another tag.

The image architecture, startup and registration must be validated against the actual published agent release. This example has not been deployed to your infrastructure by opening this guide.

Start only after confirming the agent release

```
docker compose config --quiet
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=100 agent
```

## 9. Verify with non-sensitive test data

Check that PostgreSQL is healthy and agent logs reach 'hello-ack received: agent is authenticated on API'. Confirm the execution host appears online in the Hodman console with the intended project host. A running container alone is not proof of successful registration.

Assign a disposable project to this execution host. Ask it to create a small file, run a command and read the output. Verify the files are in your host workdir and that the project's generated database credentials connect from its container. Do not put production data in the first test.

Configure ingress separately, then open the project's exact preview URL from an authorized browser, including through your VPN if applicable. Verify both application HTTP access and any required WebSocket functionality. Do not construct or guess an application URL when the console provides one.

Test an internal service using narrowly scoped read access and synthetic data. Inspect what returns to the hosted conversation and model. Verify masking and filtering where required. Check that PostgreSQL, Docker and the source internal service remain unreachable from the public internet. Test backup restoration and agent key rotation before treating the host as production-ready.

## 10. Backups, updates and key rotation

Back up project workdir, apps-data, agent configuration and any ingress certificate state. Use PostgreSQL-consistent backups; copying a live database directory is not a sufficient backup plan. Protect backups as sensitive data and test restoration.

Before updating, read release notes, record the current image tag or digest and take recoverable backups. Replace AGENT_IMAGE with a confirmed version, pull and recreate the agent, then repeat the registration and test-project checks. Database migrations may prevent a simple image-only rollback. Update project runtime images separately and validate affected projects.

For key rotation, issue a new key in the console, replace the mounted key file and AGENT_KEY_UUID, recreate the agent and confirm registration. Revoke the old key according to your security procedure. If compromise is suspected, revoke access immediately and investigate the host. Never delete Docker volumes as a routine update step.

Agent update after backups and release review

```
docker compose pull agent
docker compose up -d --no-deps --force-recreate agent
docker compose logs --tail=100 agent
```

## Troubleshooting

Image not found or pull denied: confirm that the public agent release exists, its exact repository/tag and your registry/network policy. Do not confuse hodman-ai-agent, the execution service, with hodman-ai-runtime, the project container image.

Missing private key or key UUID: check AGENT_KEY_UUID, the read-only key mount and file permissions for AGENT_UID. A key changed on disk requires agent recreation because it is cached by the process.

Authentication fails or the host stays offline: verify the key was issued in the correct workspace, the chosen agent identity, outbound DNS/TLS and WebSocket support. Inspect bounded logs without posting private keys or sensitive tool output.

Docker permission denied: check the numeric socket GID and group_add. Do not fix this by making docker.sock world-writable or exposing an unauthenticated TCP Docker endpoint.

Project files missing: compare the agent-visible paths and host bind paths; verify ownership and that Docker resolves the same host directories.

Host online but preview fails: check application startup, private DNS, ingress, TLS and browser VPN access separately. Successful WSS registration does not verify application routing.

Database unavailable: check PostgreSQL health, the core network and agent database settings. Do not open port 5432 to the public internet as a workaround.
