Skip to content

feat: add plain self-hosted Docker deploy template#976

Open
LIOsDev wants to merge 2 commits into
rohitg00:mainfrom
LIOsDev:feat/generic-docker-deploy-template
Open

feat: add plain self-hosted Docker deploy template#976
LIOsDev wants to merge 2 commits into
rohitg00:mainfrom
LIOsDev:feat/generic-docker-deploy-template

Conversation

@LIOsDev

@LIOsDev LIOsDev commented Jun 26, 2026

Copy link
Copy Markdown

Summary

Adds deploy/docker/ — a deploy template for operators who already run their own Docker host with no PaaS control plane at all (a NAS, a homelab box, a Windows machine with Docker Desktop) and don't want a managed proxy or platform-specific env vars in the way.

Reuses the existing Coolify template's Dockerfile/entrypoint pattern as-is (official iiidev/iii binary via multi-stage build, HMAC secret generated and persisted on first boot, 0.0.0.0 bind override for the REST/streams workers) — the only changes are swapping Coolify's SERVICE_FQDN_*/managed-proxy wiring for plain ports: publishing, and a README written for someone with no PaaS dashboard at all.

The README also documents a failure mode I hit and want to save the next self-hoster from: if you front this with your own reverse proxy (common on a NAS that already runs one), nginx's $host variable strips the port, which silently breaks the viewer's Host-header allowlist even when VIEWER_ALLOWED_HOSTS is set correctly — use $http_host instead. This isn't obvious from the error (403 forbidden host) and cost real debugging time before tracing it to nginx's documented (if non-obvious) behavior.

Test plan

  • docker compose up -d --build on a Linux Docker host (Debian-based NAS) — image builds clean, pulls node:22-slim + iiidev/iii:0.11.2
  • First-boot log prints AGENTMEMORY_SECRET=<64 hex chars> exactly once
  • docker compose ps shows the container healthy
  • curl http://localhost:3111/agentmemory/livez returns {service:agentmemory,status:ok}
  • Not yet tested: the opt-in viewer (3113) LAN-exposure path described in the README (AGENTMEMORY_VIEWER_HOST/VIEWER_ALLOWED_HOSTS) — the REST/MCP path above is the default, tested config; the viewer opt-in is documented but unverified in this PR

Summary by CodeRabbit

  • New Features

    • Added a plain Docker deployment option for self-hosted environments.
    • Introduced a container-based AgentMemory setup with Docker image/entrypoint, persistent /data storage, and an exposed API on port 3111, including health checks.
  • Documentation

    • Updated deployment guidance to clarify platform differences, especially TLS/proxy responsibilities for plain Docker.
    • Added a step-by-step self-hosting README covering Compose configuration, viewer access modes, secret generation/rotation, and backup/restore procedures.

Adapts the existing Coolify template's Dockerfile/entrypoint pattern
(official iiidev/iii binary via multi-stage build, HMAC secret generated
and persisted on first boot, 0.0.0.0 bind override for the engine's REST/
streams workers) for operators who already run their own Docker host with
no PaaS control plane at all - a NAS, a homelab box, or a Windows machine
running Docker Desktop - and don't want Coolify-specific env vars or a
managed proxy in the way.

The README documents the one failure mode most likely to confuse a
self-hoster who fronts this with their own reverse proxy: nginx's $host
variable strips the port, which silently breaks the viewer's Host-header
allowlist even when VIEWER_ALLOWED_HOSTS is configured correctly. Use
$http_host instead.

Signed-off-by: LIOsDev <liviu.oncioiu@gmail.com>
@vercel

vercel Bot commented Jun 26, 2026

Copy link
Copy Markdown

@LIOsDev is attempting to deploy a commit to the rohitg00's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5d7b12cb-2ba1-4c97-aad1-c34997db9c08

📥 Commits

Reviewing files that changed from the base of the PR and between e46691a and 94d6729.

📒 Files selected for processing (4)
  • deploy/README.md
  • deploy/docker/README.md
  • deploy/docker/docker-compose.yml
  • deploy/docker/entrypoint.sh
✅ Files skipped from review due to trivial changes (1)
  • deploy/docker/README.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • deploy/docker/docker-compose.yml
  • deploy/docker/entrypoint.sh

📝 Walkthrough

Walkthrough

The PR adds a plain-Docker deployment path alongside managed deployment guidance. It introduces a container image, compose service, startup entrypoint, and updated deployment docs covering setup, access, secret handling, backups, and runtime notes.

Changes

Plain Docker deployment

Layer / File(s) Summary
Deployment guide
deploy/README.md
The deployment docs add plain-Docker platform guidance, describe the service outputs, and cover setup and verification steps.
Image and compose wiring
deploy/docker/Dockerfile, deploy/docker/docker-compose.yml
The Dockerfile builds the image with pinned versions, and compose publishes ports, mounts /data, adds a healthcheck, and configures logging.
Entrypoint bootstrap
deploy/docker/entrypoint.sh
The entrypoint prepares /data, rewrites iii-config.yaml, initializes /data/.hmac, exports viewer settings, and starts AgentMemory as an unprivileged process.
Deployment operations
deploy/docker/README.md
The README adds plain-Docker setup, viewer access options, secret rotation, backup and restore steps, and host-specific notes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • rohitg00/agentmemory#442: This PR also affects viewer host handling and allowlist behavior, which is referenced by the plain-Docker viewer configuration in the new entrypoint and docs.

Poem

(_/)
( •_•)
/ >🥕 I hopped through Docker with delight,
Plain ports and secrets tucked just right.
/data hums and bunnies grin,
A cozy shell for dreams within.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding a plain self-hosted Docker deploy template.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
deploy/README.md (1)

32-41: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clarify port exposure for Plain Docker.

The "never to the host" claim on line 37 is inaccurate for the Plain Docker template, which publishes 3111 directly to the host network via ports: rather than to an upstream proxy. Consider qualifying this bullet as applying to managed platforms, or add a Plain Docker exception note.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@deploy/README.md` around lines 32 - 41, Clarify the port exposure wording in
deploy/README.md so it does not claim all templates publish 3111 only to an
upstream proxy; update the TLS upstream bullet to apply specifically to managed
platforms and add a Plain Docker exception noting that its compose setup exposes
port 3111 directly to the host. Keep the existing public-port guidance
consistent with the platform-specific docs and the managed-vs-plain distinction.
🧹 Nitpick comments (3)
deploy/docker/README.md (1)

104-106: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Backup command assumes Linux/WSL2 environment.

The $(pwd) expansion and tar command work correctly in WSL2 or Linux shells as advised in the Windows section. For users on native Windows PowerShell without WSL2, this command would fail. Consider adding a brief note cross-referencing the Windows section, or provide a Windows-equivalent using %CD% or absolute paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@deploy/docker/README.md` around lines 104 - 106, The backup example currently
assumes a Linux/WSL2 shell, so update the README guidance around the docker
run/tar command to call out that it will not work unchanged in native Windows
PowerShell. Add a brief cross-reference to the Windows section near the backup
instructions, or provide a Windows-friendly equivalent using
PowerShell-compatible path syntax (for example, an absolute path or %CD%) so
users can choose the correct command based on their shell.
deploy/docker/entrypoint.sh (1)

23-24: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Recursive chown on every boot can slow startup for large /data volumes.

chown -R "$RUN_AS" "$DATA_DIR" runs on each container start, so its cost scales with the number of files under /data. On a populated volume this adds restart latency and could approach the compose start_period: 30s before livez is reachable. Since fixing ownership is only required when the mount is root-owned, consider gating the recursive walk on a sentinel (e.g. skip if /data is already owned by node).

♻️ Conditional chown
 mkdir -p "$DATA_DIR"
-chown -R "$RUN_AS" "$DATA_DIR"
+# Only fix ownership when the volume isn't already owned by the runtime user
+# (a freshly created bind mount/named volume is root-owned). Avoids a costly
+# recursive walk on every restart of a populated volume.
+if [ "$(stat -c '%U' "$DATA_DIR")" != "node" ]; then
+  chown -R "$RUN_AS" "$DATA_DIR"
+fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@deploy/docker/entrypoint.sh` around lines 23 - 24, The startup script
unconditionally performs a recursive ownership fix on every boot, which can make
container startup slow on large data volumes. Update the entrypoint logic around
mkdir -p and chown -R in the entrypoint.sh flow to skip the recursive ownership
walk unless /data actually needs it, using an ownership check or sentinel so the
chown only runs when the mount is root-owned. Keep the existing RUN_AS and
DATA_DIR handling, but gate the recursive chown to avoid repeated work on
already-correct volumes.
deploy/docker/docker-compose.yml (1)

19-21: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Optional: document the loopback-bind variant for same-host reverse proxies.

"3111:3111" publishes the REST/MCP API on 0.0.0.0 of the host, which is the intended LAN-self-host behavior and is clearly called out in the comment above. For operators who terminate TLS with a reverse proxy on the same host, binding to loopback ("127.0.0.1:3111:3111") keeps the API off the LAN entirely. Consider mentioning this alternative in the inline comment or README.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@deploy/docker/docker-compose.yml` around lines 19 - 21, The docker-compose
ports comment currently only describes the LAN/self-host bind, so update the
inline documentation around the ports mapping to also mention the loopback-only
variant for same-host reverse proxies. Reference the ports section in
docker-compose.yml and clarify that alongside "3111:3111", operators can use a
host-bound mapping like "127.0.0.1:3111:3111" when TLS is terminated locally, or
add the same note to the README if that is the preferred documentation location.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@deploy/docker/README.md`:
- Around line 56-62: The SSH/local tunnel “Option A” in the Docker README is
missing the prerequisite setup, so users are told to run the tunnel before the
viewer port is exposed. Update that section to clearly say that the compose
service must first expose the viewer port by uncommenting the 3113 mapping in
docker-compose.yml, optionally setting AGENTMEMORY_VIEWER_HOST and
VIEWER_ALLOWED_HOSTS, and restarting the stack before running the ssh -L
command. Make the prerequisite explicit in the Option A text so the sequence is
unambiguous.

In `@deploy/README.md`:
- Line 59: The README text in the platform list is outdated because “All four”
no longer matches the set after adding Plain Docker. Update the wording in the
affected README sentence to “All five” or a neutral phrase like “All of them,”
keeping the reference to the same agentmemory API and port description
unchanged.

---

Outside diff comments:
In `@deploy/README.md`:
- Around line 32-41: Clarify the port exposure wording in deploy/README.md so it
does not claim all templates publish 3111 only to an upstream proxy; update the
TLS upstream bullet to apply specifically to managed platforms and add a Plain
Docker exception noting that its compose setup exposes port 3111 directly to the
host. Keep the existing public-port guidance consistent with the
platform-specific docs and the managed-vs-plain distinction.

---

Nitpick comments:
In `@deploy/docker/docker-compose.yml`:
- Around line 19-21: The docker-compose ports comment currently only describes
the LAN/self-host bind, so update the inline documentation around the ports
mapping to also mention the loopback-only variant for same-host reverse proxies.
Reference the ports section in docker-compose.yml and clarify that alongside
"3111:3111", operators can use a host-bound mapping like "127.0.0.1:3111:3111"
when TLS is terminated locally, or add the same note to the README if that is
the preferred documentation location.

In `@deploy/docker/entrypoint.sh`:
- Around line 23-24: The startup script unconditionally performs a recursive
ownership fix on every boot, which can make container startup slow on large data
volumes. Update the entrypoint logic around mkdir -p and chown -R in the
entrypoint.sh flow to skip the recursive ownership walk unless /data actually
needs it, using an ownership check or sentinel so the chown only runs when the
mount is root-owned. Keep the existing RUN_AS and DATA_DIR handling, but gate
the recursive chown to avoid repeated work on already-correct volumes.

In `@deploy/docker/README.md`:
- Around line 104-106: The backup example currently assumes a Linux/WSL2 shell,
so update the README guidance around the docker run/tar command to call out that
it will not work unchanged in native Windows PowerShell. Add a brief
cross-reference to the Windows section near the backup instructions, or provide
a Windows-friendly equivalent using PowerShell-compatible path syntax (for
example, an absolute path or %CD%) so users can choose the correct command based
on their shell.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3cbff03f-df3a-48ef-a4db-fe419a420c59

📥 Commits

Reviewing files that changed from the base of the PR and between f6f9e3c and e46691a.

📒 Files selected for processing (5)
  • deploy/README.md
  • deploy/docker/Dockerfile
  • deploy/docker/README.md
  • deploy/docker/docker-compose.yml
  • deploy/docker/entrypoint.sh

Comment thread deploy/docker/README.md
Comment thread deploy/README.md Outdated
- Scope the TLS-upstream/never-to-host claim to managed platforms; note Plain Docker's exception
- Fix stale 'All four' platform count to 'All five'
- Document the viewer-port prerequisite for the SSH tunnel option
- Note Windows PowerShell incompatibility in the backup command
- Skip the recursive chown on /data once it is already node-owned
- Document the loopback-bind variant for same-host reverse proxies
@LIOsDev

LIOsDev commented Jun 26, 2026

Copy link
Copy Markdown
Author

Pushed 94d6729 addressing the CodeRabbit findings:

  • deploy/README.md: scoped the "TLS upstream / never to the host" claim to the four managed platforms and called out Plain Docker as the documented exception (it publishes 3111 to the host by design); fixed the stale "All four" platform count to "All five".
  • deploy/docker/README.md: Option A (SSH tunnel) now states the prerequisite of uncommenting 3113:3113 in docker-compose.yml before tunneling; the backup command now notes it assumes a Linux/WSL2 shell and points Windows PowerShell users to ${PWD} or the WSL2 notes below it.
  • deploy/docker/entrypoint.sh: the recursive chown -R on /data is now skipped once the directory is already node-owned, so it only does the costly walk on first boot.
  • deploy/docker/docker-compose.yml: added an inline comment documenting the 127.0.0.1:3111:3111 loopback-bind alternative for operators terminating TLS with their own same-host reverse proxy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant