Last active
April 19, 2026 15:01
-
-
Save worldofgeese/117ec4f349453cd3f4ff75881b0f9feb to your computer and use it in GitHub Desktop.
OpenClaw on rootless Podman
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| openclaw-gateway: | |
| build: | |
| context: . | |
| dockerfile: ./Containerfile.nix | |
| target: runtime | |
| image: openclaw-docker-openclaw-gateway | |
| container_name: openclaw-gateway | |
| restart: unless-stopped | |
| stdin_open: true | |
| tty: true | |
| shm_size: 10gb | |
| mem_limit: 24g | |
| memswap_limit: 28g | |
| cpus: 6 | |
| security_opt: | |
| - seccomp=unconfined | |
| depends_on: | |
| - docktail | |
| - podman-in-podman | |
| environment: | |
| - DOCKER_HOST=tcp://podman-in-podman:2375 | |
| - NODE_ENV=production | |
| - UMASK=002 | |
| - OPENCLAW_SKIP_SERVICE_CHECK=true | |
| - OPENCLAW_SKIP_GMAIL_WATCHER=1 | |
| - TZ=Europe/Copenhagen | |
| volumes: | |
| - openclaw_data:/home/node/.openclaw:U,Z | |
| - openclaw_config:/home/node/.config:U,Z | |
| - openclaw_local:/home/node/.local:U,Z | |
| - openclaw_ssh:/home/node/.ssh:U,Z | |
| - wiki_vault:/home/node/wiki-vault:Z | |
| - /usr/share/zoneinfo:/usr/share/zoneinfo:ro | |
| ports: | |
| - 2222:2222 # container sshd — tailnet-only via host | |
| labels: | |
| - "docktail.service.enable=true" | |
| - "docktail.service.name=openclaw" | |
| - "docktail.service.port=18790" | |
| - "docktail.service.service-protocol=https" | |
| docktail: | |
| image: ghcr.io/marvinvr/docktail:latest | |
| container_name: docktail | |
| restart: unless-stopped | |
| depends_on: | |
| - tailscale | |
| volumes: | |
| - tailscale-socket:/var/run/tailscale | |
| environment: | |
| - DOCKER_HOST=tcp://host.containers.internal:2377 | |
| - TAILSCALE_OAUTH_CLIENT_ID=${TAILSCALE_OAUTH_CLIENT_ID} | |
| - TAILSCALE_OAUTH_CLIENT_SECRET=${TAILSCALE_OAUTH_CLIENT_SECRET} | |
| podman-in-podman: | |
| image: quay.io/podman/stable | |
| container_name: podman-in-podman | |
| privileged: true | |
| environment: | |
| - _CONTAINERS_USERNS_CONFIGURED="" | |
| volumes: | |
| - podman_data:/var/lib/containers:Z | |
| security_opt: | |
| - label=disable | |
| command: ["podman", "system", "service", "-t", "0", "tcp:0.0.0.0:2375"] | |
| restart: unless-stopped |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ============================================================================= | |
| # Stage 1: Builder - Compile OpenClaw | |
| # ============================================================================= | |
| FROM docker.io/library/node:24-bookworm AS builder | |
| RUN apt-get update && apt-get install -y --no-install-recommends \ | |
| git curl ca-certificates python3 build-essential \ | |
| && rm -rf /var/lib/apt/lists/* | |
| RUN corepack enable | |
| WORKDIR /app | |
| ARG OPENCLAW_VERSION=v2026.4.15 | |
| ARG OPENCLAW_EXTENSIONS="" | |
| RUN git clone --depth 1 --branch ${OPENCLAW_VERSION} \ | |
| https://github.com/openclaw/openclaw.git . | |
| ENV CI=true | |
| # Cache pnpm store across builds; OOM guard for low-memory hosts | |
| RUN --mount=type=cache,id=openclaw-pnpm-store,target=/root/.local/share/pnpm/store,sharing=locked \ | |
| NODE_OPTIONS=--max-old-space-size=2048 pnpm install --frozen-lockfile | |
| # A2UI: stub gracefully if cross-compiling | |
| RUN pnpm canvas:a2ui:bundle || \ | |
| (echo "A2UI bundle: creating stub (non-fatal)" && \ | |
| mkdir -p src/canvas-host/a2ui && \ | |
| echo "/* A2UI bundle unavailable */" > src/canvas-host/a2ui/a2ui.bundle.js && \ | |
| echo "stub" > src/canvas-host/a2ui/.bundle.hash) | |
| # Use the upstream Docker build sequence rather than a partial manual copy. | |
| # This preserves the required post-build ordering for bundled chat metadata. | |
| RUN NODE_OPTIONS=--max-old-space-size=2048 pnpm build:docker | |
| # Force pnpm for UI build (Bun may fail on ARM/Synology architectures) | |
| ENV OPENCLAW_PREFER_PNPM=1 | |
| RUN pnpm ui:build | |
| # ============================================================================= | |
| # Stage 2: Prune - Strip dev deps and build artifacts | |
| # ============================================================================= | |
| FROM builder AS runtime-assets | |
| RUN CI=true pnpm prune --prod && \ | |
| find dist -type f \( -name '*.d.ts' -o -name '*.d.mts' -o -name '*.d.cts' -o -name '*.map' \) -delete && \ | |
| rm -rf .git node_modules/.cache src | |
| # ============================================================================= | |
| # Stage 3: Runtime - Chainguard + Nix + Devbox | |
| # ============================================================================= | |
| FROM cgr.dev/chainguard/node:latest-dev AS runtime | |
| USER root | |
| RUN apk add --no-cache curl bash xz git coreutils openssh tmux libcap | |
| ARG USER_UID=1001 | |
| ARG HOME_DIR=/home/node | |
| # Recreate node user with deterministic UID | |
| RUN deluser node 2>/dev/null || true && \ | |
| adduser -D -u ${USER_UID} -G root -h ${HOME_DIR} -s /bin/bash node | |
| # Core runtime paths | |
| RUN mkdir -p /nix /app ${HOME_DIR} && \ | |
| chown -R ${USER_UID}:0 /nix /app ${HOME_DIR} && \ | |
| chmod -R g=u /nix /app ${HOME_DIR} | |
| ENV HOME=${HOME_DIR} | |
| ENV NIX_PROFILES="/nix/var/nix/profiles/default ${HOME_DIR}/.nix-profile" | |
| ENV PATH="${HOME_DIR}/.nix-profile/bin:/nix/var/nix/profiles/default/bin:${PATH}" | |
| USER node | |
| WORKDIR ${HOME_DIR} | |
| # Install Nix + Devbox | |
| RUN curl -L https://nixos.org/nix/install | bash -s -- --no-daemon && \ | |
| . "${HOME_DIR}/.nix-profile/etc/profile.d/nix.sh" && \ | |
| nix-env -iA nixpkgs.devbox && \ | |
| command -v nix-env && \ | |
| command -v devbox | |
| USER root | |
| WORKDIR /app | |
| # Copy pruned application | |
| COPY --from=runtime-assets --chown=1001:0 /app/dist /app/dist | |
| COPY --from=runtime-assets --chown=1001:0 /app/node_modules /app/node_modules | |
| COPY --from=runtime-assets --chown=1001:0 /app/package.json /app/package.json | |
| COPY --from=runtime-assets --chown=1001:0 /app/openclaw.mjs /app/openclaw.mjs | |
| COPY --from=runtime-assets --chown=1001:0 /app/extensions /app/extensions | |
| COPY --from=runtime-assets --chown=1001:0 /app/skills /app/skills | |
| COPY --from=runtime-assets --chown=1001:0 /app/docs /app/docs | |
| RUN ln -sf /app/openclaw.mjs /usr/local/bin/openclaw && \ | |
| chmod 755 /app/openclaw.mjs | |
| USER node | |
| WORKDIR /app | |
| ENV NODE_ENV=production | |
| ENV OPENCLAW_BUNDLED_PLUGINS_DIR=/app/extensions | |
| # ============================================================================= | |
| # Entrypoint | |
| # ============================================================================= | |
| COPY --chmod=0755 <<'ENTRY' /usr/local/bin/openclaw-entrypoint | |
| #!/bin/bash | |
| set -e | |
| # Rebuild nix profile links on every boot. | |
| # ~/.local is a persistent volume that can preserve stale profile symlinks | |
| # from previous image builds. Rebuild from the nix store each time. | |
| PROFILES_DIR="$HOME/.local/state/nix/profiles" | |
| rm -rf "$PROFILES_DIR" 2>/dev/null || true | |
| mkdir -p "$PROFILES_DIR" | |
| for env in /nix/store/*-user-environment; do | |
| if [ -x "$env/bin/nix" ]; then | |
| ln -sf "$env" "$PROFILES_DIR/profile-1-link" | |
| ln -sf "profile-1-link" "$PROFILES_DIR/profile" | |
| break | |
| fi | |
| done | |
| # Source nix shell hooks | |
| if [ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]; then | |
| . "$HOME/.nix-profile/etc/profile.d/nix.sh" | |
| elif [ -f "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh" ]; then | |
| . "/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh" | |
| fi | |
| # Ensure devbox is on PATH | |
| if ! command -v devbox >/dev/null 2>&1; then | |
| DEVBOX_STORE="$(find /nix/store -maxdepth 1 -type d -name '*-devbox-*' 2>/dev/null | head -n 1)" | |
| if [ -n "$DEVBOX_STORE" ] && [ -x "$DEVBOX_STORE/bin/devbox" ]; then | |
| export PATH="$DEVBOX_STORE/bin:$PATH" | |
| fi | |
| fi | |
| # Optional persistent Devbox environment | |
| DEVBOX_DIR="$HOME/.openclaw/devbox-env" | |
| if [ -f "$DEVBOX_DIR/devbox.json" ]; then | |
| if command -v devbox >/dev/null 2>&1; then | |
| eval "$(cd "$DEVBOX_DIR" && devbox shellenv 2>/dev/null)" | |
| else | |
| echo "Warning: devbox not found in PATH" >&2 | |
| fi | |
| fi | |
| # Start sshd if configured | |
| SSHD_CONFIG="$HOME/.config/sshd/sshd_config" | |
| if [ -f "$SSHD_CONFIG" ]; then | |
| SSHD_BIN="$(command -v sshd 2>/dev/null)" | |
| if [ -n "$SSHD_BIN" ]; then | |
| "$SSHD_BIN" -f "$SSHD_CONFIG" -E "$HOME/.config/sshd/sshd.log" | |
| fi | |
| fi | |
| exec "$@" | |
| ENTRY | |
| ENTRYPOINT ["/usr/local/bin/openclaw-entrypoint"] | |
| CMD ["node", "openclaw.mjs", "gateway", "--allow-unconfigured"] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment