fix(deploy): use network_mode host to fix loopback guard on Linux Docker (#4973)

* fix(deploy): use network_mode host to fix loopback guard on Linux Docker

* fix(deploy): address review feedback — wire OD_PORT and tighten auth note

* fix(deploy): wire OD_PORT in Linux override and include linux override in systemd unit and updater

* fix(deploy): reject unsupported Compose implementations for Linux override

* fix(deploy): lock OD_BIND_HOST to loopback for host networking in Linux

---------

Co-authored-by: kemycrome <luca.gernone@gmail.com>
This commit is contained in:
Gianluca Gernone
2026-07-03 03:05:22 +00:00
committed by GitHub
co-authored by kemycrome
parent fcc67b5a4c
commit 353efbad01
4 changed files with 102 additions and 7 deletions
+6
View File
@@ -18,6 +18,12 @@ OD_API_TOKEN=
# Optional escape hatch for deployments whose reverse proxy already authenticates
# every request before it reaches the daemon. Set to 1 only for trusted setups;
# when enabled, the daemon skips OD_API_TOKEN enforcement entirely.
#
# Note: connector endpoints (Composio, GitHub OAuth) also require the daemon to
# receive requests over loopback. On Linux Docker this is handled automatically
# by docker-compose.linux.yml (network_mode: host). On macOS/Windows use
# a reverse proxy bound to 127.0.0.1.
OPEN_DESIGN_DISABLE_API_AUTH=
# Container memory limit. The idle service has been verified around 18-22 MiB.
+37
View File
@@ -0,0 +1,37 @@
# Linux override for docker-compose.yml.
#
# Switches to host networking so the daemon sees browser connections as
# 127.0.0.1, satisfying the loopback guard on connector endpoints
# (Composio key save, GitHub OAuth, etc.) that bridge networking breaks.
#
# network_mode: host is Linux-only. macOS and Windows Docker Desktop users
# should use the base docker-compose.yml behind a loopback-bound reverse proxy.
#
# Used automatically by install.sh on Linux. To apply manually:
# docker compose -f docker-compose.yml -f docker-compose.linux.yml up -d
name: open-design
services:
open-design:
# Cap CPU so a busy agent run does not starve the host.
cpus: '1.5'
# Drop the bridge port mapping — host networking makes it irrelevant.
ports: !reset []
network_mode: host
environment:
# Re-declare both port vars so the daemon listener follows OPEN_DESIGN_PORT
# even without the bridge mapping (which normally enforced the remap).
OD_PORT: ${OPEN_DESIGN_PORT:-7456}
OD_WEB_PORT: ${OPEN_DESIGN_PORT:-7456}
# With host networking the daemon binds directly on the host's interfaces.
# Lock it to loopback so the base file's 0.0.0.0 default does not expose
# Open Design on the LAN or public IP.
OD_BIND_HOST: 127.0.0.1
healthcheck:
# Use wget (available in Alpine) instead of node — faster and does not
# compete with the daemon for CPU during startup.
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:${OPEN_DESIGN_PORT:-7456}/api/health"]
interval: 30s
timeout: 10s
retries: 5
start_period: 20s
+37 -7
View File
@@ -19,10 +19,15 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
DEPLOY_DIR="$(dirname "$SCRIPT_DIR")"
COMPOSE_FILE="${DEPLOY_DIR}/docker-compose.yml"
OVERRIDE_FILE="${DEPLOY_DIR}/docker-compose.override.yml"
LINUX_OVERRIDE_FILE="${DEPLOY_DIR}/docker-compose.linux.yml"
# Build the -f argument list: always include the base file,
# and add the override if it exists (used by tests for isolation).
# add the Linux host-network override, and add docker-compose.override.yml if present
# (used by tests for isolation).
COMPOSE_FILES=(-f "$COMPOSE_FILE")
if [ "$(uname -s)" = "Linux" ] && [ -f "$LINUX_OVERRIDE_FILE" ]; then
COMPOSE_FILES+=(-f "$LINUX_OVERRIDE_FILE")
fi
if [ -f "$OVERRIDE_FILE" ]; then
COMPOSE_FILES+=(-f "$OVERRIDE_FILE")
fi
@@ -259,6 +264,26 @@ if ! detect_compose; then
exit 1
fi
# The Linux host-network override uses the !reset YAML tag (Docker Compose
# v2.17+). Reject legacy docker-compose v1 and podman-compose early so users
# get a clear message instead of a YAML parse error mid-install.
if [ "$OS" = "Linux" ] && [ -f "$LINUX_OVERRIDE_FILE" ]; then
if [ "$COMPOSE_CMD" != "docker compose" ]; then
error "The Linux host-network override requires 'docker compose' v2."
error "Found: ${COMPOSE_CMD}"
step "Install the Docker Compose plugin: https://docs.docker.com/compose/install/"
exit 1
fi
_compose_ver="$($COMPOSE_CMD version --short 2>/dev/null || echo "0.0.0")"
_compose_major="$(echo "$_compose_ver" | cut -d. -f1)"
_compose_minor="$(echo "$_compose_ver" | cut -d. -f2)"
if [ "$_compose_major" -lt 2 ] || { [ "$_compose_major" -eq 2 ] && [ "$_compose_minor" -lt 17 ]; }; then
error "Docker Compose v2.17 or later required for the Linux override (found v${_compose_ver})."
step "Upgrade: https://docs.docker.com/compose/install/"
exit 1
fi
fi
if ! check_runtime_running; then
warn "${CONTAINER_RUNTIME} is not running."
case "$DISTRO" in
@@ -464,13 +489,18 @@ if [ "$OS" = "Linux" ] && [ "$OPT_NO_SYSTEMD" = "0" ]; then
echo "Type=oneshot"
echo "RemainAfterExit=yes"
echo "WorkingDirectory=${DEPLOY_DIR}"
if [ -f "$OVERRIDE_FILE" ]; then
echo "ExecStart=${CONTAINER_BIN} compose -f ${COMPOSE_FILE} -f ${OVERRIDE_FILE} up -d --no-build"
echo "ExecStop=${CONTAINER_BIN} compose -f ${COMPOSE_FILE} -f ${OVERRIDE_FILE} down"
else
echo "ExecStart=${CONTAINER_BIN} compose -f ${COMPOSE_FILE} up -d --no-build"
echo "ExecStop=${CONTAINER_BIN} compose -f ${COMPOSE_FILE} down"
# Build the compose -f list to match what the installer used at install
# time, so systemd restarts use the same file set (including the Linux
# host-network override when present).
_COMPOSE_ARGS="-f ${COMPOSE_FILE}"
if [ -f "$LINUX_OVERRIDE_FILE" ]; then
_COMPOSE_ARGS="${_COMPOSE_ARGS} -f ${LINUX_OVERRIDE_FILE}"
fi
if [ -f "$OVERRIDE_FILE" ]; then
_COMPOSE_ARGS="${_COMPOSE_ARGS} -f ${OVERRIDE_FILE}"
fi
echo "ExecStart=${CONTAINER_BIN} compose ${_COMPOSE_ARGS} up -d --no-build"
echo "ExecStop=${CONTAINER_BIN} compose ${_COMPOSE_ARGS} down"
echo "TimeoutStartSec=120"
echo ""
echo "[Install]"
+22
View File
@@ -9,9 +9,13 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
DEPLOY_DIR="$(dirname "$SCRIPT_DIR")"
COMPOSE_FILE="${DEPLOY_DIR}/docker-compose.yml"
OVERRIDE_FILE="${DEPLOY_DIR}/docker-compose.override.yml"
LINUX_OVERRIDE_FILE="${DEPLOY_DIR}/docker-compose.linux.yml"
HEALTH_TIMEOUT=60
COMPOSE_FILES=(-f "$COMPOSE_FILE")
if [ "$(uname -s)" = "Linux" ] && [ -f "$LINUX_OVERRIDE_FILE" ]; then
COMPOSE_FILES+=(-f "$LINUX_OVERRIDE_FILE")
fi
if [ -f "$OVERRIDE_FILE" ]; then
COMPOSE_FILES+=(-f "$OVERRIDE_FILE")
fi
@@ -48,6 +52,24 @@ else
exit 1
fi
# Same guard as install.sh: the Linux override uses !reset (Docker Compose v2.17+).
if [ "$(uname -s)" = "Linux" ] && [ -f "$LINUX_OVERRIDE_FILE" ]; then
if [ "$COMPOSE_CMD" != "docker compose" ]; then
error "The Linux host-network override requires 'docker compose' v2."
error "Found: ${COMPOSE_CMD}"
step "Install the Docker Compose plugin: https://docs.docker.com/compose/install/"
exit 1
fi
_compose_ver="$($COMPOSE_CMD version --short 2>/dev/null || echo "0.0.0")"
_compose_major="$(echo "$_compose_ver" | cut -d. -f1)"
_compose_minor="$(echo "$_compose_ver" | cut -d. -f2)"
if [ "$_compose_major" -lt 2 ] || { [ "$_compose_major" -eq 2 ] && [ "$_compose_minor" -lt 17 ]; }; then
error "Docker Compose v2.17 or later required for the Linux override (found v${_compose_ver})."
step "Upgrade: https://docs.docker.com/compose/install/"
exit 1
fi
fi
OPT_IMAGE=""
NON_INTERACTIVE=0