diff --git a/services/n8n/.env b/services/n8n/.env new file mode 100644 index 0000000..66cfcd1 --- /dev/null +++ b/services/n8n/.env @@ -0,0 +1,33 @@ +#version=1.1 +#URL=https://github.com/tailscale-dev/ScaleTail +#COMPOSE_PROJECT_NAME= # Optional: only use when running multiple deployments on the same infrastructure. + +# Service Configuration +SERVICE=n8n # Service name (e.g., adguard). Used as hostname in Tailscale and for container naming (app-${SERVICE}). +IMAGE_URL=docker.n8n.io/n8nio/n8n:stable # Docker image URL from container registry (e.g., adguard/adguard-home). +TAILNET_NAME= # Webhook URL for Tailscale. + +# Network Configuration +SERVICEPORT=5678 # Port to expose to local network. Uncomment the "ports:" section in compose.yaml to enable. +DNS_SERVER=9.9.9.9 # Preferred DNS server for Tailscale. Uncomment the "dns:" section in compose.yaml to enable. + +# Tailscale Configuration +TS_AUTHKEY= # Auth key from https://tailscale.com/admin/authkeys. See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions. + +# Optional Service variables +# PUID=1000 + +#Time Zone setting for containers +TZ=Europe/Amsterdam # See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones + +# Any Container environment variables are declared below. See https://docs.docker.com/compose/how-tos/environment-variables/ + +# N8N Configuration variables +POSTGRES_USER= # Postgres username for n8n database. +POSTGRES_PASSWORD= # Postgres password for n8n database. +POSTGRES_DB= # Postgres database name for n8n. + +POSTGRES_NON_ROOT_USER= # Postgres non-root user for n8n database. +POSTGRES_NON_ROOT_PASSWORD= # Postgres non-root password for n8n database. + +N8N_ENCRYPTION_KEY= # Encryption key for n8n. \ No newline at end of file diff --git a/services/n8n/README.md b/services/n8n/README.md new file mode 100644 index 0000000..1b50fe2 --- /dev/null +++ b/services/n8n/README.md @@ -0,0 +1,37 @@ +# SERVICE with Tailscale Sidecar Configuration + +This Docker Compose configuration sets up [N8N](https://n8n.io/) with Tailscale as a sidecar container to keep the app reachable over your Tailnet. + +## SERVICE + +[N8N](https://n8n.io/) a workflow automation platform that uniquely combines AI capabilities with business process automation, giving technical teams the flexibility of code with the speed of no code. + +## Configuration Overview + +In this setup, the `tailscale-N8N` service runs Tailscale, which manages secure networking for SERVICE. The `SERVICE` service utilizes the Tailscale network stack via Docker's `network_mode: service:` configuration. This keeps the app Tailnet-only unless you intentionally expose ports. + +For the folder structure you will need to pre-create the n8n-storage directory, and correct the ownership before the first run, by doing the following: + +```bash +mkdir -p n8n-storage +sudo chown -R 1000:1000 n8n-storage +``` + +## What to document for users + +In this setup, the tailscale-n8n service runs Tailscale, which manages secure networking for the N8N service. The N8N service uses the Tailscale network stack via Docker’s network_mode: service: configuration. This setup ensures that N8N management interface is only accessible through the Tailscale network (or locally, if preferred), providing an extra layer of security and privacy for managing your automations. + +If you need the runners for N8N uncomment the following within the docker-compose.yaml file - `N8N_RUNNERS_MODE=external`, `N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}`, `N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0` as well as uncommenting `RUNNERS_AUTH_TOKEN` within the .env file. + +The configs section also needs to be updated to match the below. Specifically the Proxy port needs changing from 8080 to 5678 + +```plain +configs: + ts-serve: + content: | + {"TCP":{"443":{"HTTPS":true}}, + "Web":{"$${TS_CERT_DOMAIN}:443": + {"Handlers":{"/": + {"Proxy":"http://127.0.0.1:5678"}}}}, + "AllowFunnel":{"$${TS_CERT_DOMAIN}:443":false}} +``` diff --git a/services/n8n/compose.yml b/services/n8n/compose.yml new file mode 100644 index 0000000..8395d2e --- /dev/null +++ b/services/n8n/compose.yml @@ -0,0 +1,97 @@ +configs: + ts-serve: + content: | + {"TCP":{"443":{"HTTPS":true}}, + "Web":{"$${TS_CERT_DOMAIN}:443": + {"Handlers":{"/": + {"Proxy":"http://127.0.0.1:5678"}}}}, + "AllowFunnel":{"$${TS_CERT_DOMAIN}:443":false}} + +services: + # Make sure you have updated/checked the .env file with the correct variables. + # All the ${ xx } need to be defined there. + # Tailscale Sidecar Configuration + tailscale: + image: tailscale/tailscale:latest # Image to be used + container_name: tailscale-${SERVICE} # Name for local container management + hostname: ${SERVICE} # Name used within your Tailscale environment + environment: + - TS_AUTHKEY=${TS_AUTHKEY} + - TS_STATE_DIR=/var/lib/tailscale + - TS_SERVE_CONFIG=/config/serve.json # Tailscale Serve configuration to expose the web interface on your local Tailnet - remove this line if not required + - TS_USERSPACE=false + - TS_ENABLE_HEALTH_CHECK=true # Enable healthcheck endpoint: "/healthz" + - TS_LOCAL_ADDR_PORT=127.0.0.1:41234 # The : for the healthz endpoint + #- TS_ACCEPT_DNS=true # Uncomment when using MagicDNS + - TS_AUTH_ONCE=true + configs: + - source: ts-serve + target: /config/serve.json + volumes: + - ./config:/config # Config folder used to store Tailscale files - you may need to change the path + - ./ts/state:/var/lib/tailscale # Tailscale requirement - you may need to change the path + devices: + - /dev/net/tun:/dev/net/tun # Network configuration for Tailscale to work + cap_add: + - net_admin # Tailscale requirement + #ports: + # - 0.0.0.0:${SERVICEPORT}:${SERVICEPORT} # Binding port ${SERVICE}PORT to the local network - may be removed if only exposure to your Tailnet is required + # If any DNS issues arise, use your preferred DNS provider by uncommenting the config below + # dns: + # - ${DNS_SERVER} + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"] # Check Tailscale has a Tailnet IP and is operational + interval: 1m # How often to perform the check + timeout: 10s # Time to wait for the check to succeed + retries: 3 # Number of retries before marking as unhealthy + start_period: 10s # Time to wait before starting health checks + restart: always + + postgres: + container_name: n8n-postgres + image: postgres:16 + restart: always + environment: + - POSTGRES_USER + - POSTGRES_PASSWORD + - POSTGRES_DB + - POSTGRES_NON_ROOT_USER + - POSTGRES_NON_ROOT_PASSWORD + volumes: + - ./${SERVICE}-db:/var/lib/postgresql/data + - ./init-data.sh:/docker-entrypoint-initdb.d/init-data.sh + healthcheck: + test: ['CMD-SHELL', 'pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}'] + interval: 5s + timeout: 5s + retries: 10 + + application: + image: ${IMAGE_URL} + network_mode: service:tailscale # Sidecar configuration to route ${SERVICE} through Tailscale + container_name: app-${SERVICE} + restart: unless-stopped + volumes: + - ./${SERVICE}-storage:/home/node/.n8n + environment: + - N8N_HOST=n8n.${TAILNET_NAME} + - N8N_PORT=5678 + - N8N_PROTOCOL=https + - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY} + - N8N_RUNNERS_ENABLED=true + - N8N_BLOCK_ENV_ACCESS_IN_NODE=true + - N8N_GIT_NODE_DISABLE_BARE_REPOS=true + - NODE_ENV=production + - WEBHOOK_URL=https://${TAILNET_NAME}/ + - DB_TYPE=postgresdb + - DB_POSTGRESDB_HOST=postgres + - DB_POSTGRESDB_PORT=5432 + - DB_POSTGRESDB_DATABASE=${POSTGRES_DB} + - DB_POSTGRESDB_USER=${POSTGRES_NON_ROOT_USER} + - DB_POSTGRESDB_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD} + # - N8N_RUNNERS_MODE=external + # - N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN} + # - N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0 + depends_on: + postgres: + condition: service_healthy \ No newline at end of file