Skip to content

feat(deploy): fly.io / Railway / Render one-click templates#361

Merged
rohitg00 merged 10 commits into
mainfrom
feat/deploy-templates-343
May 14, 2026
Merged

feat(deploy): fly.io / Railway / Render one-click templates#361
rohitg00 merged 10 commits into
mainfrom
feat/deploy-templates-343

Conversation

@rohitg00
Copy link
Copy Markdown
Owner

@rohitg00 rohitg00 commented May 13, 2026

Closes #343.

Adds deploy/ with three one-click templates so users can stand up
agentmemory on managed infrastructure without rolling their own Docker
host. Each template extends the published
rohitghumare64/agentmemory:latest image, mounts persistent storage at
/data, generates the HMAC secret on first boot, and defaults
AGENTMEMORY_REQUIRE_HTTPS=1.

Platforms

Platform Deploy button URL
fly.io https://fly.io/launch?repo=https://github.com/rohitg00/agentmemory&path=deploy/fly
Railway https://railway.com/new/template?template=https%3A%2F%2Fgithub.com%2Frohitg00%2Fagentmemory&rootDirectory=deploy%2Frailway
Render https://render.com/deploy?repo=https%3A%2F%2Fgithub.com%2Frohitg00%2Fagentmemory&branch=main

Shield buttons linking to those URLs live in the new ## Deploy
section of the main README.

HMAC-first-boot pattern

Each template ships an entrypoint.sh that runs ahead of the
agentmemory binary:

  1. Look for ${AGENTMEMORY_HMAC_FILE} (default /data/.hmac).
  2. If absent, openssl rand -hex 32 produces a 256-bit secret, write
    it to that file with umask 077 + chmod 600, and print
    AGENTMEMORY_SECRET=<value> to stdout exactly once. The operator
    captures it from the platform's deploy logs.
  3. Export AGENTMEMORY_SECRET and exec agentmemory "$@".

Subsequent boots load the existing secret silently. The secret is
never written to a config file, never set as a platform env var, never
hard-coded anywhere in the templates. Rotation is a one-liner:
rm /data/.hmac and restart — the next boot prints a fresh value.

AGENTMEMORY_REQUIRE_HTTPS=1 default

Every template bakes AGENTMEMORY_REQUIRE_HTTPS=1 into the Dockerfile
ENV plus the platform-native config ([env] in fly.toml, envVars
in render.yaml, the deploy-time env on Railway). If a TLS termination
upstream gets misconfigured and clients suddenly find themselves
talking plaintext HTTP, the v0.9.12 plaintext-bearer guard in the
integration plugins will refuse to send the bearer token to a
non-loopback host instead of silently leaking it.

Viewer port stays internal

Only port 3111 (REST API) is published. The viewer on 3113 stays
bound to the container's localhost. Each platform's README documents
the SSH-tunnel pattern for reaching it:

  • fly.io: fly proxy 3113:3113
  • Railway: railway ssh --service agentmemory -- -L 3113:localhost:3113
  • Render: ssh srv-XXYYZZ@ssh.<region>.render.com -L 3113:localhost:3113

File layout

deploy/
  README.md                top-level index, platform comparison table
  fly/
    fly.toml               app + [[mounts]] + [http_service] + [env] + [[vm]]
    Dockerfile             FROM rohitghumare64/agentmemory:latest + entrypoint
    entrypoint.sh          first-boot HMAC generator
    README.md              flyctl flow, capture, rotation, backup, cost
  railway/
    railway.json           builder=DOCKERFILE + healthcheckPath + restartPolicy
    Dockerfile             same FROM + entrypoint pattern
    entrypoint.sh
    README.md              dashboard + CLI flow, volume add, viewer SSH
  render/
    render.yaml            type: web, runtime: docker, plan: starter, disk
    Dockerfile             same FROM + entrypoint pattern
    entrypoint.sh
    README.md              Blueprint flow, deploy hook, disk snapshots

Verification

  • bash -n clean on all three entrypoint.sh
  • json.load on railway.json parses
  • render.yaml follows the current Blueprint spec (runtime: docker,
    disk.{name,mountPath,sizeGB}, healthCheckPath)
  • fly.toml matches the current schema (primary_region,
    [[mounts]], [http_service] with force_https = true +
    auto_stop_machines = "stop" + auto_start_machines = true +
    min_machines_running = 0)
  • npm test baseline confirmed: the 10 pre-existing failures in
    test/mcp-standalone.test.ts reproduce on main without these
    changes — none of the deploy/ additions touch Node source.

Out of scope

Publishing rohitghumare64/agentmemory:latest itself (multi-arch
amd64+arm64). The templates reference it; the image build workflow is
a follow-up.

Summary by CodeRabbit

  • New Features

    • One‑click deployment templates for Fly.io, Railway, Render, and Coolify that produce self‑contained images, mount persistent /data, generate a one‑time HMAC secret on first boot (printed once), and enforce HTTPS by default; only port 3111 is public, viewer stays loopback‑bound on 3113 (SSH‑tunnel documented)
  • Documentation

    • Expanded per‑platform guides, comparison table, badges/blueprints, healthchecks, secret capture/rotation, backup/restore, migration and operational notes

Review Change Stack

Closes #343.

Lets operators stand up agentmemory on managed infrastructure without
rolling their own Docker host. Each template extends the published
rohitghumare64/agentmemory:latest image, mounts persistent storage at
/data, generates the HMAC secret on first boot via openssl rand into
/data/.hmac (chmod 600, printed once to stdout), and ships
AGENTMEMORY_REQUIRE_HTTPS=1 by default so the v0.9.12 plaintext-bearer
guard refuses to leak tokens over non-loopback HTTP if a TLS upstream
gets misconfigured.

Only port 3111 (REST API) is exposed publicly. The viewer on 3113
stays bound to localhost inside the container; every README documents
the SSH-tunnel pattern. The main README gains a Deploy section with
three Deploy-to-platform shield buttons linking to the platform's
URL-encoded one-click flow, plus the deploy/ subtree with per-platform
docs covering HMAC capture, rotation, /data backup, viewer access,
cost floor, and known caveats.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agentmemory Ready Ready Preview, Comment May 14, 2026 10:22am

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds one-click deploy templates and documentation for Fly.io, Railway, Render, and Coolify. Each template provides a Dockerfile that builds agentmemory and bundles the iii binary at build time, an entrypoint that generates a one-time HMAC secret persisted to /data and exported as AGENTMEMORY_SECRET, platform config files, and per-platform READMEs covering deploy, secret capture/rotation, viewer access, and backups.

Changes

One-Click Deploy Templates

One-Click Deploy Templates

Layer / File(s) Summary
Deploy documentation and shared framework
README.md, deploy/README.md
Top-level README adds a Deploy section and badges; deploy/README.md documents shared guarantees: Dockerfile-based builds pulling @agentmemory/agentmemory, bundling iii, persistent /data, first-boot HMAC generation to /data/.hmac, AGENTMEMORY_REQUIRE_HTTPS=1, and port exposure rules (3111 public, 3113 loopback).
Fly.io deployment template
deploy/fly/fly.toml, deploy/fly/Dockerfile, deploy/fly/entrypoint.sh, deploy/fly/README.md
Fly template includes fly.toml (build, volume, service, healthcheck), Dockerfile that installs iii and @agentmemory/agentmemory, entrypoint that generates/loads /data/.hmac and exports AGENTMEMORY_SECRET, and README with flyctl flow, secret capture, viewer proxy, rotation, backups, and caveats.
Railway deployment template
deploy/railway/railway.json, deploy/railway/Dockerfile, deploy/railway/entrypoint.sh, deploy/railway/README.md
Railway template provides railway.json (build, replicas, healthcheck, restart policy), Dockerfile building iii and agentmemory, entrypoint managing the one-time HMAC secret, and README documenting dashboard/CLI deploy, secret capture, viewer access, rotation, and backups.
Render deployment template
deploy/render/render.yaml, deploy/render/Dockerfile, deploy/render/entrypoint.sh, deploy/render/README.md
Render template adds render.yaml for a Web Service with a persistent disk at /data, Dockerfile bundling iii and agentmemory, entrypoint persisting a one-time HMAC secret, and README covering Blueprint/Deploy Hook flows, secret capture/rotation, SSH viewer tunneling, backups, and platform caveats.
Coolify deployment template
deploy/coolify/docker-compose.yml, deploy/coolify/Dockerfile, deploy/coolify/entrypoint.sh, deploy/coolify/README.md
Coolify template provides a Docker Compose stack and Dockerfile that install iii and agentmemory, an entrypoint that creates/reads /data/.hmac and exports AGENTMEMORY_SECRET, and README covering deploy, secret capture, healthcheck, viewer access options, rotation, and backups.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • #301: One-click deploy templates: overlap on deployment entrypoint and /data ownership fixes — both add entrypoint logic and fix persistent /data ownership/secret initialization.

"🐰
A secret pops at boot — one-time light,
Three clouds ready to host tonight,
Port three-one-one opens wide,
Viewer stays tucked on the inside,
Hop deploy, keep data tight."

🚥 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 'feat(deploy): fly.io / Railway / Render one-click templates' directly and accurately summarizes the main change: adding one-click deploy templates for three platforms.
Linked Issues check ✅ Passed All primary objectives from #343 are met: deploy/ structure with fly/railway/render subdirs, persistent /data mounting, first-boot HMAC generation, AGENTMEMORY_REQUIRE_HTTPS=1 default, port 3111 public/3113 internal, README documentation with deploy buttons and operational guidance.
Out of Scope Changes check ✅ Passed All changes are directly aligned with deploying agentmemory to fly.io, Railway, Render, and Coolify. No unrelated modifications detected outside the deploy/ directory and top-level README updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/deploy-templates-343

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (2)
deploy/render/entrypoint.sh (1)

20-32: ⚡ Quick win

Validate the generated secret before persisting.

Although set -e will catch most openssl failures, consider adding explicit validation to ensure SECRET is non-empty and matches the expected 64-character hex format before writing it to disk. This prevents edge cases where openssl succeeds but outputs unexpected data.

🛡️ Proposed validation
 if [ ! -s "${HMAC_FILE}" ]; then
   SECRET="$(openssl rand -hex 32)"
+  if [ -z "${SECRET}" ] || ! printf '%s' "${SECRET}" | grep -Eq '^[0-9a-f]{64}$'; then
+    echo "ERROR: Failed to generate valid HMAC secret" >&2
+    exit 1
+  fi
   umask 077
   printf '%s\n' "${SECRET}" > "${HMAC_FILE}"
🤖 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/render/entrypoint.sh` around lines 20 - 32, Validate the generated
SECRET before persisting: after generating SECRET (variable name SECRET in
entrypoint.sh) check that it is non-empty and matches the expected 64-character
hex pattern (e.g. regex ^[0-9a-f]{64}$) and only then write to HMAC_FILE; if
validation fails, log an error message including SECRET context (but not the
secret itself if sensitive) and exit non-zero (or retry generation a few times),
ensuring HMAC_FILE is never created with invalid content.
deploy/railway/entrypoint.sh (1)

20-32: ⚡ Quick win

Validate the generated secret before persisting.

Although set -e will catch most openssl failures, consider adding explicit validation to ensure SECRET is non-empty and matches the expected 64-character hex format before writing it to disk. This prevents edge cases where openssl succeeds but outputs unexpected data.

🛡️ Proposed validation
 if [ ! -s "${HMAC_FILE}" ]; then
   SECRET="$(openssl rand -hex 32)"
+  if [ -z "${SECRET}" ] || ! printf '%s' "${SECRET}" | grep -Eq '^[0-9a-f]{64}$'; then
+    echo "ERROR: Failed to generate valid HMAC secret" >&2
+    exit 1
+  fi
   umask 077
   printf '%s\n' "${SECRET}" > "${HMAC_FILE}"
🤖 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/railway/entrypoint.sh` around lines 20 - 32, The startup script writes
an HMAC secret to HMAC_FILE without validating the SECRET value; before
persisting in the if block that generates SECRET, validate that SECRET is
non-empty and matches the expected 64-character hex pattern (e.g., 32 bytes ->
64 hex chars) and fail fast if it does not; update the block that sets SECRET
(variable name SECRET) so after calling openssl rand -hex 32 you check a regex
like /^[0-9a-f]{64}$/ and only write to HMAC_FILE and print the copy notice when
the validation passes, otherwise log an error and exit with non-zero status.
🤖 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/fly/Dockerfile`:
- Around line 9-11: The Dockerfile currently sets USER root and makes
/usr/local/bin/agentmemory-entrypoint.sh executable but never switches to a
non-root runtime user; update the Dockerfile to create or use a non-root user
(e.g., add a dedicated user/group) and set appropriate ownership (chown) on
files like /usr/local/bin/agentmemory-entrypoint.sh, then add an explicit USER
<non-root> directive before the ENTRYPOINT to ensure the container runs with
least privilege; refer to the existing USER root and agentmemory-entrypoint.sh
symbols and ensure the new USER directive appears after file permissions are set
and before ENTRYPOINT.

In `@deploy/fly/README.md`:
- Around line 24-39: The README uses a hardcoded Fly app name "agentmemory" in
commands like the fly launch --name agentmemory, fly volumes create
agentmemory_data --region iad --size 1, and fly logs --app agentmemory | grep
-A1 AGENTMEMORY_SECRET= which will fail for users when the name is taken; update
these commands to use a clear placeholder or variable (e.g., <app-name> or
$FLY_APP) consistently across the README so users can substitute their own
unique app name and corresponding volume/log commands (ensure the volume name
and any --app flags reference the same placeholder).

In `@deploy/railway/Dockerfile`:
- Around line 9-11: Remove the unsafe "USER root" line and instead ensure the
entrypoint script is installed with the executable bit and correct ownership
(use COPY --chmod=0755 to place agentmemory-entrypoint.sh and chown to a
non-root user), create or use an explicit non-root user in the Dockerfile (e.g.,
adduser/addgroup) and set USER to that non-root user; update any references to
root-owned files so they are owned by the non-root user and ensure
agentmemory-entrypoint.sh remains executable for that user.

In `@deploy/railway/README.md`:
- Around line 74-79: The README demonstrates a non-working SSH port-forward
command; replace the invalid `railway ssh --service agentmemory -- -L
3113:localhost:3113` usage with guidance that Railway's `railway ssh` does not
support `-L` flags and offer the supported alternatives: instruct maintainers to
use Railway's TCP Proxy to expose port 3113, configure a public URL for the
service in Railway settings, or—if true SSH tunneling is required—run an sshd
in-container and use native `ssh -L` against that host; update the example lines
that mention the failing command and browser access so they point to one of
these valid approaches instead of the invalid `railway ssh -L` invocation.

In `@deploy/render/Dockerfile`:
- Around line 7-11: The Dockerfile currently switches to USER root to run chmod
on /usr/local/bin/agentmemory-entrypoint.sh and never switches back, leaving the
container running as root; fix this by removing the USER root + RUN chmod step
and instead use COPY --chmod=0755 for agentmemory-entrypoint.sh (keeping the
entrypoint path /usr/local/bin/agentmemory-entrypoint.sh) so no root shell is
needed—if the base image/docker version doesn't support COPY --chmod, create or
reference the non-root user used by the base image (e.g., nonroot), use chown to
assign the file to that user and switch back to that non-root user after
adjusting permissions so the container does not run as root.

In `@README.md`:
- Around line 541-543: The README's "Deploy to Render" badge is misleading
because Render requires render.yaml at the repository root and the current
render.yaml lives in deploy/render/, so either move deploy/render/render.yaml to
the repository root (rename/move the file) or remove/update the "Deploy to
Render" anchor and image (the Render badge/link in README) so it is not shown;
update references accordingly to prevent users from clicking a button that will
fail.

---

Nitpick comments:
In `@deploy/railway/entrypoint.sh`:
- Around line 20-32: The startup script writes an HMAC secret to HMAC_FILE
without validating the SECRET value; before persisting in the if block that
generates SECRET, validate that SECRET is non-empty and matches the expected
64-character hex pattern (e.g., 32 bytes -> 64 hex chars) and fail fast if it
does not; update the block that sets SECRET (variable name SECRET) so after
calling openssl rand -hex 32 you check a regex like /^[0-9a-f]{64}$/ and only
write to HMAC_FILE and print the copy notice when the validation passes,
otherwise log an error and exit with non-zero status.

In `@deploy/render/entrypoint.sh`:
- Around line 20-32: Validate the generated SECRET before persisting: after
generating SECRET (variable name SECRET in entrypoint.sh) check that it is
non-empty and matches the expected 64-character hex pattern (e.g. regex
^[0-9a-f]{64}$) and only then write to HMAC_FILE; if validation fails, log an
error message including SECRET context (but not the secret itself if sensitive)
and exit non-zero (or retry generation a few times), ensuring HMAC_FILE is never
created with invalid content.
🪄 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: a3da4ba6-6184-4f71-ad00-0f7470bdd04c

📥 Commits

Reviewing files that changed from the base of the PR and between 96c0ed0 and 9c5f909.

📒 Files selected for processing (14)
  • README.md
  • deploy/README.md
  • deploy/fly/Dockerfile
  • deploy/fly/README.md
  • deploy/fly/entrypoint.sh
  • deploy/fly/fly.toml
  • deploy/railway/Dockerfile
  • deploy/railway/README.md
  • deploy/railway/entrypoint.sh
  • deploy/railway/railway.json
  • deploy/render/Dockerfile
  • deploy/render/README.md
  • deploy/render/entrypoint.sh
  • deploy/render/render.yaml

Comment thread deploy/fly/Dockerfile Outdated
Comment thread deploy/fly/README.md Outdated
Comment thread deploy/railway/Dockerfile Outdated
Comment thread deploy/railway/README.md Outdated
Comment thread deploy/render/Dockerfile Outdated
Comment thread README.md Outdated
…der badge

Addresses CodeRabbit findings on PR #361.

Dockerfiles (fly/railway/render): drop `USER root` + RUN chmod — those
left the container running as root after build. Replaced with
`COPY --chmod=0755 --chown=65532:65532` so the entrypoint script lands
with the correct permissions and owner without ever switching off the
distroless image's default nonroot user. Added an explicit
`USER 65532:65532` directive after COPY to make the runtime user
intent-visible to reviewers.

fly/README.md: every command referenced the hardcoded app name
`agentmemory`. Since the global `agentmemory` slug on Fly is almost
certainly taken, the first `fly launch --name agentmemory` fails and
the rest of the volume/log commands point at a non-existent app. Walk
users through setting `APP="agentmemory-$(whoami)"` once at the top and
reference `$APP` everywhere (launch / volumes / logs / proxy / ssh /
backup) — and derive the volume name (`agentmemory_data` style) from
`$APP` so it stays consistent.

railway/README.md: the previous `railway ssh --service agentmemory --
-L 3113:localhost:3113` snippet was invalid — Railway's CLI ssh does
not support port forwarding. Replaced with a quick in-container curl
check plus two valid browser paths: Railway TCP Proxy (the recommended
option, dashboard → Networking → TCP Proxy on container port 3113) and
an in-container sshd over a TCP Proxy if true SSH tunneling is needed.

README.md: removed the "Deploy to Render" badge. Render's deploy URL
requires `render.yaml` at the repository root, which we keep clean.
The `deploy/render/` blueprint stays in-tree for users who set up the
Render Blueprint flow manually — the deploy section now points readers
there explicitly.

Skipped: the entrypoint.sh SECRET-shape regex check. `openssl rand
-hex 32` under `set -eu` already exits non-zero on any failure path
and always emits exactly 64 hex characters when it succeeds — a
post-hoc regex would only fire on a kernel-level surprise we can't
recover from anyway.
Templates previously did `FROM rohitghumare64/agentmemory:latest`, an
image that does not exist on Docker Hub. agentmemory ships via npm
(`@agentmemory/agentmemory`) and runs against the `iii` engine binary
fetched from the `iii-hq/iii` GitHub release. There is no first-party
agentmemory Docker image yet.

Rewrote all three Dockerfiles to be self-contained against
`node:22-slim`:

- `apt-get` curl / openssl / ca-certificates / tini for the runtime
  prereqs (openssl for the first-boot HMAC, tini for clean PID 1).
- Download the iii binary from
  `github.com/iii-hq/iii/releases/download/iii/v${III_VERSION}/...`
  matching the build host's arch (uname -m).
- `npm install -g @agentmemory/agentmemory@${AGENTMEMORY_VERSION}` with
  `--omit=optional` so the CJK native deps (added in PR #362) don't
  fail the build on platforms without musl/glibc prebuilds.
- `mkdir -p /data && chown node:node /data` so the existing
  first-boot entrypoint can write `/data/.hmac` without root.
- `USER node:node` (UID 1000, the standard non-root user in the node
  image) instead of the made-up `65532:65532` that assumed a
  distroless base.

Both `AGENTMEMORY_VERSION` and `III_VERSION` are `ARG`s so platform
operators can pin a specific release through their dashboard's
build-args UI without editing the Dockerfile.

README updates:

- Top-level `README.md` and `deploy/README.md` no longer claim users
  pull a pre-published image; the wording now matches what each
  template actually does (build from source on the platform's builder).
- Per-platform "Known caveats" sections drop the
  `rohitghumare64/agentmemory:latest` line and replace it with notes
  about build-time, cache reuse, and how to pin via build args.
- `deploy/render/README.md` drops the obsolete `&imgURL=...` Deploy
  Hook query-string trick and replaces it with
  `AGENTMEMORY_VERSION` build-arg guidance.
- `deploy/README.md` switches "Integration clients (Hermes, OpenClaw,
  pi)" → "Integration plugins" to match the scrub convention from the
  earlier PR-body edit.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/fly/Dockerfile`:
- Around line 14-22: The Dockerfile currently streams the remote release tarball
into tar (using III_TAR and III_VERSION to construct the URL) without verifying
integrity; update the RUN block that downloads and installs /usr/local/bin/iii
to first fetch a checksum (or a detached signature), verify the downloaded
artifact against that checksum/signature and fail the build on mismatch, then
extract and install; reference the variables III_TAR and III_VERSION and ensure
the verification step exits non‑zero on failure before the tar | tar -xz -C
/usr/local/bin/ and chmod +x /usr/local/bin/iii steps are executed.
🪄 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: 58844904-ccfd-4589-a0f9-c5f3fdca2c44

📥 Commits

Reviewing files that changed from the base of the PR and between 6865585 and 1dbcf19.

📒 Files selected for processing (8)
  • README.md
  • deploy/README.md
  • deploy/fly/Dockerfile
  • deploy/fly/README.md
  • deploy/railway/Dockerfile
  • deploy/railway/README.md
  • deploy/render/Dockerfile
  • deploy/render/README.md
✅ Files skipped from review due to trivial changes (2)
  • deploy/render/README.md
  • deploy/railway/README.md

Comment thread deploy/fly/Dockerfile Outdated
Adds deploy/coolify/ alongside the existing fly / Railway / Render
templates. Coolify (https://coolify.io/self-hosted) is the
self-hosted-on-your-own-VPS option for operators who don't want their
memories on a third-party managed plane.

Same self-contained Dockerfile pattern as the other three (node:22-slim
+ iii binary + npm package, USER node, build-args for version pinning)
plus a docker-compose.yml that Coolify's "Docker Compose" build pack
consumes directly. Compose layer adds a HEALTHCHECK, a named volume
for /data, and json-file log rotation matching the rest of the deploy
surface.

README walks the operator through:

- Coolify dashboard flow (New Application -> Public Repository ->
  Docker Compose build pack -> Base Directory: deploy/coolify).
- Capturing the first-boot HMAC secret from the Logs tab.
- Viewer access via SSH tunnel from the Coolify host (port 3113 stays
  internal by default) and the optional second-domain + basic-auth
  pattern for sharing it.
- HMAC rotation, /data backup paths (Restic / Borg / rsync / Coolify
  Backups), and VPS cost-floor pointers (Hetzner CX22, DO Basic
  Droplet, Vultr).

Top-level deploy/README.md and README.md updated to list the new
template and explain when to pick it (already running a VPS, want a
self-hosted control plane, don't want a third-party host holding your
memories).
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
deploy/coolify/README.md (1)

26-29: 💤 Low value

Add language identifier to fenced code block.

The markdown linter flags this code fence for missing a language specifier. Since it's a URL, adding text satisfies the linter without changing rendering.

📝 Proposed fix
-   ```
+   ```text
    https://github.com/rohitg00/agentmemory
    ```

As per coding guidelines, static analysis tools flagged MD040 (fenced-code-language).

🤖 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/coolify/README.md` around lines 26 - 29, The fenced code block
containing the URL "https://github.com/rohitg00/agentmemory" is missing a
language identifier and triggers MD040; update that triple-backtick fence to
include a language specifier (use "text") so the block becomes ```text ... ```,
leaving the URL content unchanged.
deploy/coolify/docker-compose.yml (1)

7-8: ⚡ Quick win

Consider making version args overridable via environment variables.

Hardcoding AGENTMEMORY_VERSION and III_VERSION in the compose file means operators must edit the file to change versions. Coolify supports passing environment variables into build args.

♻️ Proposed refactor to support environment overrides
      args:
-        AGENTMEMORY_VERSION: "0.9.12"
-        III_VERSION: "0.11.2"
+        AGENTMEMORY_VERSION: "${AGENTMEMORY_VERSION:-0.9.12}"
+        III_VERSION: "${III_VERSION:-0.11.2}"

Operators can then set AGENTMEMORY_VERSION=0.9.13 in the Coolify environment tab to override without touching the compose file.

🤖 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/coolify/docker-compose.yml` around lines 7 - 8, Replace the hardcoded
compose variables AGENTMEMORY_VERSION and III_VERSION with
environment-overridable values by referencing shell/env substitutions (e.g.,
${AGENTMEMORY_VERSION:-0.9.12} and ${III_VERSION:-0.11.2}) so operators can set
AGENTMEMORY_VERSION and III_VERSION in the environment/Coolify UI to override
defaults; update any build args or image tags that currently use
AGENTMEMORY_VERSION and III_VERSION to use these substitution tokens instead and
document the new env vars.
deploy/coolify/entrypoint.sh (1)

34-35: ⚡ Quick win

Add explicit error handling when reading the HMAC file.

If ${HMAC_FILE} is deleted or becomes unreadable between the initial check and line 34, cat will fail with a generic error due to set -e. An explicit check with a descriptive message improves operational diagnostics.

🛡️ Proposed fix to add error message
+if [ ! -r "${HMAC_FILE}" ]; then
+  echo "ERROR: HMAC file ${HMAC_FILE} is missing or unreadable." >&2
+  exit 1
+fi
 AGENTMEMORY_SECRET="$(cat "${HMAC_FILE}")"
 export AGENTMEMORY_SECRET
🤖 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/coolify/entrypoint.sh` around lines 34 - 35, The current assignment
AGENTMEMORY_SECRET="$(cat "${HMAC_FILE}")" can fail with a generic error if
${HMAC_FILE} is removed or unreadable; add an explicit check and a clear error
path: verify the file exists and is readable (using a test on "${HMAC_FILE}")
and if not, write a descriptive error to stderr and exit non‑zero, otherwise
read the file into AGENTMEMORY_SECRET and export it; reference
AGENTMEMORY_SECRET and HMAC_FILE in the fix and ensure the script still exits on
failure.
🤖 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/coolify/Dockerfile`:
- Around line 14-22: The build is using ARCH=$(uname -m) which reads the build
host, breaking buildx cross-compilation; switch to using Docker build args/vars
TARGETARCH (or TARGETPLATFORM) in the case that sets III_TAR and in the curl URL
— replace the ARCH variable and its case on x86_64/aarch64 with a case on
${TARGETARCH} (map amd64 -> iii-x86_64-unknown-linux-gnu.tar.gz and arm64 ->
iii-aarch64-unknown-linux-gnu.tar.gz), use ${III_TAR} and ${III_VERSION} in the
download line, and optionally provide a fallback to uname -m if TARGETARCH is
not set to preserve local builds.

---

Nitpick comments:
In `@deploy/coolify/docker-compose.yml`:
- Around line 7-8: Replace the hardcoded compose variables AGENTMEMORY_VERSION
and III_VERSION with environment-overridable values by referencing shell/env
substitutions (e.g., ${AGENTMEMORY_VERSION:-0.9.12} and ${III_VERSION:-0.11.2})
so operators can set AGENTMEMORY_VERSION and III_VERSION in the
environment/Coolify UI to override defaults; update any build args or image tags
that currently use AGENTMEMORY_VERSION and III_VERSION to use these substitution
tokens instead and document the new env vars.

In `@deploy/coolify/entrypoint.sh`:
- Around line 34-35: The current assignment AGENTMEMORY_SECRET="$(cat
"${HMAC_FILE}")" can fail with a generic error if ${HMAC_FILE} is removed or
unreadable; add an explicit check and a clear error path: verify the file exists
and is readable (using a test on "${HMAC_FILE}") and if not, write a descriptive
error to stderr and exit non‑zero, otherwise read the file into
AGENTMEMORY_SECRET and export it; reference AGENTMEMORY_SECRET and HMAC_FILE in
the fix and ensure the script still exits on failure.

In `@deploy/coolify/README.md`:
- Around line 26-29: The fenced code block containing the URL
"https://github.com/rohitg00/agentmemory" is missing a language identifier and
triggers MD040; update that triple-backtick fence to include a language
specifier (use "text") so the block becomes ```text ... ```, leaving the URL
content unchanged.
🪄 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: a9334f5c-1496-430f-a523-871b8a1ca742

📥 Commits

Reviewing files that changed from the base of the PR and between 1dbcf19 and 6a18e7c.

📒 Files selected for processing (6)
  • README.md
  • deploy/README.md
  • deploy/coolify/Dockerfile
  • deploy/coolify/README.md
  • deploy/coolify/docker-compose.yml
  • deploy/coolify/entrypoint.sh
✅ Files skipped from review due to trivial changes (2)
  • README.md
  • deploy/README.md

Comment thread deploy/coolify/Dockerfile Outdated
Replaces the curl + tar + chmod sequence in all four Dockerfiles
(fly / railway / render / coolify) with a single multi-stage
COPY --from=iiidev/iii:${III_VERSION} /app/iii /usr/local/bin/iii.

Why:

- Drops the supply-chain risk CodeRabbit flagged on PR #361
  (curl-and-extract without checksum verification). iiidev/iii is the
  same image agentmemory's docker-compose.yml already pulls — using
  Docker Hub's content-addressed manifest as the integrity boundary
  is the same trust we already extend on the local Compose path.
- Drops the arch case statement (BuildKit picks the right manifest
  entry automatically from the multi-arch tag).
- Drops 3 of the 4 apt-get packages from the bookkeeping side; curl
  stays for the eventual HEALTHCHECK probe.

The iii binary lives at /app/iii inside the distroless iiidev/iii
image (per github.com/iii-hq/iii engine/Dockerfile), and COPY into
node:22-slim lands it at /usr/local/bin/iii with 755 permissions so
any USER can exec it.

ARG III_VERSION is now declared before the first FROM so it can be
interpolated into the iii-image stage tag. ARG AGENTMEMORY_VERSION
stays in the final stage where the npm install consumes it.
Audit against fly.io / Railway / Render / Coolify docs and the
agentmemory CLI source surfaced four bugs that would prevent every
template from actually serving traffic on a managed host. This commit
addresses all four together because they share the same fix surface
(entrypoint + Dockerfile + platform config) and shipping them
independently would leave intermediate states broken.

1. iii config bound 127.0.0.1 + used relative ./data paths.

   The npm-bundled @agentmemory/agentmemory/dist/iii-config.yaml hard-
   codes `host: 127.0.0.1` on iii-http and `file_path: ./data/...` on
   iii-state / iii-stream. agentmemory CLI's findIiiConfig() returns
   the first existing candidate from a 3-item list with the npm bundle
   at position 0, so a sibling iii-config.yaml in cwd does not
   override. On any managed platform the result is the REST API
   binding loopback (router can't reach) and state writing to a
   directory that's not the persistent mount.

   Fix: the entrypoint overwrites the bundled file at boot with a
   container-tuned config (host 0.0.0.0, /data/... absolute paths,
   no iii-exec because the CLI orchestrates the worker over WS).
   This needs the entrypoint to run as root, hence the Dockerfile
   change below.

2. /data volume permissions root:root vs USER node.

   Every managed platform mounts persistent volumes root-owned 0755.
   The earlier Dockerfile ran `RUN mkdir -p /data && chown -R node`
   at build time, but the volume overlay at runtime replaces /data
   with the platform's empty root-owned mountpoint, leaving the
   `node` USER unable to write state_store.db / .hmac.

   Fix: stay USER root through the image, add `gosu` to apt, do the
   chown in entrypoint.sh against the *runtime* /data, then drop to
   the unprivileged `node` user via `exec gosu node:node agentmemory`
   before exec'ing the CLI. Symmetric to the iii-init busybox sidecar
   pattern in the repo-root docker-compose.yml.

3. Render's PORT injection broke the published port.

   Render auto-sets PORT (default 10000) and routes the public proxy
   to that port; the container then must bind 0.0.0.0:$PORT. Our
   container always binds 3111 (Dockerfile EXPOSE + iii-http config),
   so Render's proxy was forwarding to 10000 and getting connection
   refused.

   Fix: render.yaml now sets envVars PORT=3111 to override Render's
   default, plus exposes AGENTMEMORY_VERSION / III_VERSION as envVars
   (Render translates envVars into docker build args automatically).
   Also drops the AGENTMEMORY_REQUIRE_HTTPS / AGENTMEMORY_HMAC_FILE /
   AGENTMEMORY_DATA_DIR entries — server-side code never reads any of
   them; entrypoint reads HMAC_FILE / DATA_DIR via ${VAR:-default}
   so defaults already apply.

4. Coolify compose `ports:` bypassed Traefik.

   Per Coolify docs, `ports: ["3111:3111"]` binds the port directly on
   the host outside of the proxy network. The deploy would have been
   reachable on `http://<host>:3111` with no TLS termination and no
   domain routing.

   Fix: replaced `ports:` with `expose:` so the port is only
   reachable on the internal proxy network. Operator now sets the
   service domain in the Coolify UI as `https://<fqdn>:3111` (the
   `:3111` is Coolify's proxy hint, not a public port). Also added
   `SERVICE_FQDN_AGENTMEMORY_3111` to the environment so the
   container can know its public URL via Coolify's magic-env
   convention.

Other minor fixes:

- railway.json adds `requiredMountPath: /data` so Railway fails fast
  if the operator forgets to attach a volume.
- fly.toml drops the dead-letter [env] block (server code does not
  read AGENTMEMORY_REQUIRE_HTTPS / AGENTMEMORY_HMAC_FILE /
  AGENTMEMORY_DATA_DIR; entrypoint defaults handle the latter two).
- All 4 entrypoints unified — same script, same heredoc, copy with
  `cp -p` at build time.
- READMEs updated to reflect the new bound port story, the manual
  Render Blueprint flow (Render's deploy-button auto-detection only
  scans repo-root render.yaml), the Coolify proxy / domain pattern,
  and the gosu privilege drop.

Best-practice audit results (fly.toml, railway.json, render.yaml,
docker-compose.yml) verified against current platform docs — fly.toml
schema is clean, railway.json gets the requiredMountPath addition,
render.yaml + coolify compose get the rewires above.
agentmemory@0.9.12 declares `iii-sdk: ^0.11.2`, which `npm install`
caret-resolves to **0.11.6** as of writing. That bumps the worker
SDK ahead of the engine the agentmemory repo pins (iii v0.11.2).
The two versions are wire-compatible in the calls agentmemory uses
today, but the policy intent in the repo is single-version lockstep
until the v0.11.6 sandbox-everything model is wired through — running
SDK 0.11.6 against engine 0.11.2 in production silently re-introduces
the very drift the AGENTMEMORY_III_VERSION pin exists to prevent.

`npm install -g` ignores the `overrides` field, so we cannot fix this
with the existing global install. Switch each Dockerfile to a local
install under /opt/agentmemory with a one-line package.json carrying:

    overrides: { iii-sdk: 0.11.2 }

then symlink the produced `node_modules/.bin/agentmemory` into
`/usr/local/bin/agentmemory` so the entrypoint invocation stays
identical. Verified locally that this resolves
`node_modules/iii-sdk/package.json` to version 0.11.2 (was 0.11.6
without the override).

Side effects:
- Entrypoint III_CONFIG path moves from
  `/usr/local/lib/node_modules/@agentmemory/agentmemory/dist/iii-config.yaml`
  (global) to
  `/opt/agentmemory/node_modules/@agentmemory/agentmemory/dist/iii-config.yaml`
  (local). agentmemory CLI's findIiiConfig() resolves __dirname
  through the symlink to the local install so candidate-0 lookup
  still hits the file we overwrite.
- New ARG `III_SDK_VERSION=0.11.2` declared in every Dockerfile,
  surfaced as a `build.args` field in coolify's compose and as an
  envVar in render.yaml so operators can pin a different SDK version
  if they manually migrate to engine 0.11.6+ down the line.
- AGENTMEMORY_III_VERSION env now baked into the image (matches the
  engine pin) so the CLI's iii-binary download fallback path also
  resolves to the right version if PATH lookup ever misses.
End-to-end deploy to fly.io surfaced the warning:

    [WARN  tini (645)] Tini is not running as PID 1 and isn't
    registered as a child subreaper. Zombie processes will not be
    re-parented to Tini, so zombie reaping won't work.

Fly's init system holds PID 1, so tini lands as a child process and
the agentmemory CLI (which spawns iii detached) ends up with no
process reaping its eventual zombies. Setting TINI_SUBREAPER=1
triggers prctl(PR_SET_CHILD_SUBREAPER, 1) so tini still reaps
descendant zombies even when it isn't PID 1. Same fix applies to
Railway and Render (which also wrap our entrypoint under their own
init) and to Coolify when the operator runs the container under any
PID-1-grabbing supervisor like systemd-nspawn.

Verified end-to-end against fly.io:

- App agentmemory-ghumare64 deployed via remote builder
- 114 MB image (iii engine + node:22-slim + npm packages)
- 1 GB volume in iad provisioned and mounted
- First-boot HMAC generated and persisted to /data/.hmac
- iii-engine + agentmemory worker came up clean
- Worker registered with iii engine over ws://localhost:49134
- /agentmemory/livez returned 200 in 220 ms
- Bearer auth gating verified: 401 without secret, 200/201 with it
- POST /agentmemory/observe ingested a synthetic observation
- POST /agentmemory/smart-search returned a valid response envelope

Only outstanding log noise after this commit is a Node punycode
deprecation warning from a transitive dep — not actionable from this
side.
Previous commit missed these two — same reasoning applies (Railway and
Render both wrap our entrypoint under their own platform init, so tini
lands as a non-PID-1 child and zombie reaping needs the prctl
subreaper bit set).
Real end-to-end deploy on fly.io surfaced four refinements that
generalise across the other three templates. This commit ports them.

1. Health-check grace_period: 10 s -> 30 s.

   Measured cold start on a fresh fly.io machine in `iad`:

       machine image prepared :  5.1 s
       volume mount + format  :  2.5 s
       firecracker boot       :  1.0 s
       entrypoint + chown     :  0.5 s
       iii-engine ready       :  3.0 s
       agentmemory worker reg :  2.0 s
       --------------------------------
       healthcheck passes     : ~9-10 s

   `grace_period = "10s"` (fly.toml) and `start_period: 20s` (coolify
   compose + Dockerfile HEALTHCHECK) were both within the noise of the
   measured time. Bumped both to 30 s — gives a 3x safety margin
   without affecting normal-operation health-check cadence.

   Railway and Render manage their own initial-deploy grace windows
   server-side, so railway.json's `healthcheckTimeout: 30` (per-attempt)
   and render.yaml's `healthCheckPath` stay as-is.

2. Documented LLM + embedding provider env-var menu in deploy/README.md.

   Live deploy logs surface a clear warning:

       [agentmemory] No LLM provider key found
       (ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENROUTER_API_KEY,
        MINIMAX_API_KEY). LLM-backed compression and summarization are
       DISABLED -- using no-op provider.

   The deploy READMEs never told operators how to opt in. Added a
   table covering ANTHROPIC / GEMINI / OPENROUTER for LLM,
   OPENAI / VOYAGE for embeddings, plus AGENTMEMORY_AUTO_COMPRESS and
   AGENTMEMORY_INJECT_CONTEXT for the corresponding behaviour flags.
   Each platform's variable-injection surface (flyctl secrets,
   dashboard Environment tab) is named in the same table.

3. Captured cold-start budget in deploy/README.md.

   Same step-by-step measured timing above is now in the shared
   README so operators have a concrete expectation rather than
   guessing why first-byte after `fly deploy` takes ten seconds. Notes
   that every template's start window is sized for 3x of this.

4. fly README: documented shared-IPv4 + dedicated-IPv6 default.

   Fly assigns one of each by default at no charge. Legacy clients
   without SNI that need a dedicated IPv4 can buy one for $2/month
   via `fly ips allocate-v4`. Added the build-time stats observed in
   the real deploy (~30 s first build, ~10 s cached rebuild, 114 MB
   image) to the Known Caveats section so operators sizing
   build-minute budgets have real numbers.

No code paths changed -- only TOML / YAML / Markdown surface tweaks
and one HEALTHCHECK `start_period` bump in the coolify Dockerfile.
@rohitg00 rohitg00 merged commit a9c3a59 into main May 14, 2026
5 checks passed
@rohitg00 rohitg00 deleted the feat/deploy-templates-343 branch May 14, 2026 10:23
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.

One-click deploy templates: fly.io / Railway / Render

1 participant