Shell script for n8n pod and containers
This commit is contained in:
136
bin/create_pod_n8n.sh
Executable file
136
bin/create_pod_n8n.sh
Executable file
@@ -0,0 +1,136 @@
|
||||
#!/bin/bash
|
||||
|
||||
# To be run by user n8n to create the pod and containers with
|
||||
# n8n (main) + n8n Task Runners sidecar, bind-mounted data/files dirs,
|
||||
# and systemd user services.
|
||||
|
||||
set -e
|
||||
|
||||
# Environment variables
|
||||
POD_NAME='n8n_pod'
|
||||
N8N_CTR_NAME='n8n_ctr'
|
||||
RUNNERS_CTR_NAME='n8n_runners_ctr'
|
||||
# Images (version must match between n8n and runners)
|
||||
N8N_IMAGE='docker.io/n8nio/n8n:1.121.3'
|
||||
RUNNERS_IMAGE='docker.io/n8nio/runners:1.121.3'
|
||||
HOST_LOCAL_IP='127.0.0.1'
|
||||
N8N_HOST_PORT='8087'
|
||||
N8N_CONTAINER_PORT='5678'
|
||||
BIND_DIR="$HOME/.local/share/$POD_NAME"
|
||||
# Container /home/node/.n8n holds workflows, credentials, config
|
||||
N8N_DATA_DIR="$BIND_DIR/data"
|
||||
# Container /files for the “Read/Write Files from Disk” node
|
||||
N8N_FILES_DIR="$BIND_DIR/files"
|
||||
USER_SYSTEMD_DIR="$HOME/.config/systemd/user"
|
||||
# Timezone
|
||||
TZ_VALUE='Europe/Berlin'
|
||||
GENERIC_TZ_VALUE='Europe/Berlin'
|
||||
# Shared secret between n8n and runners
|
||||
RUNNERS_AUTH_TOKEN='xqw97zn23vlmqxawezor87b2o2xmn873z'
|
||||
# Optional JS allow-lists.
|
||||
# NOTE: imports are *disabled by default*; you can relax these later.
|
||||
NODE_BUILTIN_MODULES='crypto,fs,path,buffer,stream,url,util'
|
||||
# e.g. "moment,uuid" once added to the runners image
|
||||
NODE_EXTERNAL_MODULES=''
|
||||
|
||||
# Prepare directories
|
||||
mkdir -p "$N8N_DATA_DIR" "$N8N_FILES_DIR" "$USER_SYSTEMD_DIR"
|
||||
# Fix permissions for n8n user (node:1000 in container)
|
||||
podman unshare chown 1000:1000 "$N8N_DATA_DIR" "$N8N_FILES_DIR"
|
||||
|
||||
# Create pod if not yet existing
|
||||
if ! podman pod exists "$POD_NAME"; then
|
||||
podman pod create -n "$POD_NAME" \
|
||||
-p "$HOST_LOCAL_IP:$N8N_HOST_PORT:$N8N_CONTAINER_PORT"
|
||||
echo "Pod '$POD_NAME' created (rc=$?)"
|
||||
else
|
||||
echo "Pod '$POD_NAME' already exists."
|
||||
fi
|
||||
|
||||
# n8n main container
|
||||
# Remove any old container
|
||||
podman rm -f "$N8N_CTR_NAME"
|
||||
# New Container
|
||||
podman run -d --name "$N8N_CTR_NAME" --pod "$POD_NAME" \
|
||||
-e TZ="$TZ_VALUE" \
|
||||
-e GENERIC_TIMEZONE="$GENERIC_TZ_VALUE" \
|
||||
-e N8N_PORT="$N8N_CONTAINER_PORT" \
|
||||
-e N8N_PROTOCOL='http' \
|
||||
-e N8N_HOST='localhost' \
|
||||
-e WEBHOOK_URL="http://localhost:${N8N_HOST_PORT}" \
|
||||
-e N8N_RESTRICT_FILE_ACCESS_TO='/files' \
|
||||
-e N8N_RUNNERS_ENABLED='true' \
|
||||
-e N8N_RUNNERS_MODE='external' \
|
||||
-e N8N_RUNNERS_AUTH_TOKEN="$RUNNERS_AUTH_TOKEN" \
|
||||
-e N8N_RUNNERS_BROKER_LISTEN_ADDRESS='0.0.0.0' \
|
||||
-e N8N_NATIVE_PYTHON_RUNNER='false' \
|
||||
-e QUEUE_HEALTH_CHECK_ACTIVE='true' \
|
||||
-e N8N_METRICS='false' \
|
||||
-v "$N8N_DATA_DIR:/home/node/.n8n:Z" \
|
||||
-v "$N8N_FILES_DIR:/files:Z" \
|
||||
"$N8N_IMAGE"
|
||||
echo "Container '$N8N_CTR_NAME' started (rc=$?)"
|
||||
|
||||
# n8n Task Runners container (only for JS, not for Python)
|
||||
# Remove any old container
|
||||
podman rm -f "$RUNNERS_CTR_NAME"
|
||||
# New Container
|
||||
# Python runner is effectively disabled because N8N_NATIVE_PYTHON_RUNNER=false on n8n.
|
||||
podman run -d --name "$RUNNERS_CTR_NAME" --pod "$POD_NAME" \
|
||||
-e TZ="$TZ_VALUE" \
|
||||
-e GENERIC_TIMEZONE="$GENERIC_TZ_VALUE" \
|
||||
-e N8N_RUNNERS_AUTH_TOKEN="$RUNNERS_AUTH_TOKEN" \
|
||||
-e N8N_RUNNERS_TASK_BROKER_URI='http://127.0.0.1:5679' \
|
||||
-e N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT='15' \
|
||||
-e NODE_FUNCTION_ALLOW_BUILTIN="$NODE_BUILTIN_MODULES" \
|
||||
-e NODE_FUNCTION_ALLOW_EXTERNAL="$NODE_EXTERNAL_MODULES" \
|
||||
"$RUNNERS_IMAGE"
|
||||
echo "Container '$RUNNERS_CTR_NAME' started (rc=$?)"
|
||||
|
||||
# Wait for API readiness (/health)
|
||||
CHECK_URL="http://$HOST_LOCAL_IP:$N8N_HOST_PORT"
|
||||
echo -n "Waiting for n8n API at $CHECK_URL ..."
|
||||
for attempt in $(seq 1 30); do
|
||||
if curl -fsS "$CHECK_URL" >/dev/null 2>&1; then
|
||||
echo "ready."
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
if [ "$attempt" -eq 30 ]; then
|
||||
echo "timeout error." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Generate systemd service files
|
||||
cd "$USER_SYSTEMD_DIR"
|
||||
podman generate systemd --name --new --files "$POD_NAME"
|
||||
echo "Generated systemd service files (rc=$?)"
|
||||
|
||||
# Stop & remove live pod and containers
|
||||
podman pod stop --ignore --time 15 "$POD_NAME"
|
||||
podman pod rm -f --ignore "$POD_NAME"
|
||||
if podman pod exists "$POD_NAME"; then
|
||||
echo "ERROR: Pod $POD_NAME still exists." >&2
|
||||
exit 1
|
||||
else
|
||||
echo "Stopped & removed live pod $POD_NAME and containers."
|
||||
fi
|
||||
|
||||
# Enable systemd user services
|
||||
systemctl --user daemon-reload
|
||||
# pod service (creates pod + containers)
|
||||
systemctl --user enable --now "pod-${POD_NAME}.service"
|
||||
systemctl --user is-enabled "pod-${POD_NAME}.service"
|
||||
systemctl --user is-active "pod-${POD_NAME}.service"
|
||||
echo "Enabled systemd service pod-${POD_NAME}.service (rc=$?)"
|
||||
echo "To view status: systemctl --user status pod-${POD_NAME}.service"
|
||||
echo "To view logs: journalctl --user -u pod-${POD_NAME}.service -f"
|
||||
|
||||
# Optional: also manage containers individually (generated by podman generate systemd)
|
||||
systemctl --user enable --now "container-${N8N_CTR_NAME}.service"
|
||||
systemctl --user enable --now "container-${RUNNERS_CTR_NAME}.service"
|
||||
echo "To view n8n logs : journalctl --user -u container-${N8N_CTR_NAME}.service -f"
|
||||
echo "To view runners logs : journalctl --user -u container-${RUNNERS_CTR_NAME}.service -f"
|
||||
|
||||
echo "n8n API is reachable at http://$HOST_LOCAL_IP:$N8N_HOST_PORT"
|
||||
Reference in New Issue
Block a user