first commit
Some checks failed
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Some checks failed
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
35
docker/images/n8n-base/Dockerfile
Normal file
35
docker/images/n8n-base/Dockerfile
Normal file
@@ -0,0 +1,35 @@
|
||||
ARG NODE_VERSION=24.13.1
|
||||
|
||||
FROM dhi.io/node:${NODE_VERSION}-alpine3.22-dev
|
||||
|
||||
ARG NODE_VERSION
|
||||
|
||||
# Install all dependencies in a single layer to minimize image size
|
||||
RUN apk add --no-cache busybox-binsh && \
|
||||
# Install fonts
|
||||
apk --no-cache add --virtual .build-deps-fonts msttcorefonts-installer fontconfig && \
|
||||
update-ms-fonts && \
|
||||
fc-cache -f && \
|
||||
apk del .build-deps-fonts && \
|
||||
find /usr/share/fonts/truetype/msttcorefonts/ -type l -exec unlink {} \; && \
|
||||
# Install OS dependencies
|
||||
apk update && \
|
||||
apk upgrade --no-cache && \
|
||||
apk add --no-cache \
|
||||
git \
|
||||
openssh \
|
||||
openssl \
|
||||
graphicsmagick=1.3.45-r0 `# pinned to avoid ghostscript-fonts (AGPL)` \
|
||||
tini \
|
||||
tzdata \
|
||||
ca-certificates \
|
||||
libc6-compat && \
|
||||
# Cleanup
|
||||
rm -rf /tmp/* /root/.npm /root/.cache/node /opt/yarn* && \
|
||||
apk del apk-tools
|
||||
|
||||
WORKDIR /home/node
|
||||
# DHI images use a non-standard global npm path, so we need to set NODE_PATH
|
||||
# to allow externally installed npm packages to be found by require()
|
||||
ENV NODE_PATH=/opt/nodejs/node-v${NODE_VERSION}/lib/node_modules
|
||||
EXPOSE 5678/tcp
|
||||
32
docker/images/n8n/Dockerfile
Normal file
32
docker/images/n8n/Dockerfile
Normal file
@@ -0,0 +1,32 @@
|
||||
ARG NODE_VERSION=24.13.1
|
||||
ARG N8N_VERSION=snapshot
|
||||
|
||||
FROM n8nio/base:${NODE_VERSION}
|
||||
|
||||
ARG N8N_VERSION
|
||||
ARG N8N_RELEASE_TYPE=dev
|
||||
ENV NODE_ENV=production
|
||||
ENV N8N_RELEASE_TYPE=${N8N_RELEASE_TYPE}
|
||||
ENV SHELL=/bin/sh
|
||||
|
||||
WORKDIR /home/node
|
||||
|
||||
COPY ./compiled /usr/local/lib/node_modules/n8n
|
||||
COPY docker/images/n8n/docker-entrypoint.sh /
|
||||
|
||||
RUN cd /usr/local/lib/node_modules/n8n && \
|
||||
npm rebuild sqlite3 && \
|
||||
ln -s /usr/local/lib/node_modules/n8n/bin/n8n /usr/local/bin/n8n && \
|
||||
mkdir -p /home/node/.n8n && \
|
||||
chown -R node:node /home/node && \
|
||||
rm -rf /root/.npm /tmp/*
|
||||
|
||||
EXPOSE 5678/tcp
|
||||
USER node
|
||||
ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"]
|
||||
|
||||
LABEL org.opencontainers.image.title="n8n" \
|
||||
org.opencontainers.image.description="Workflow Automation Tool" \
|
||||
org.opencontainers.image.source="https://github.com/n8n-io/n8n" \
|
||||
org.opencontainers.image.url="https://n8n.io" \
|
||||
org.opencontainers.image.version=${N8N_VERSION}
|
||||
255
docker/images/n8n/README.md
Normal file
255
docker/images/n8n/README.md
Normal file
@@ -0,0 +1,255 @@
|
||||

|
||||
|
||||
# n8n - Secure Workflow Automation for Technical Teams
|
||||
|
||||
n8n is a workflow automation platform that gives technical teams the flexibility of code with the speed of no-code. With 400+ integrations, native AI capabilities, and a fair-code license, n8n lets you build powerful automations while maintaining full control over your data and deployments.
|
||||
|
||||

|
||||
|
||||
## Key Capabilities
|
||||
|
||||
- **Code When You Need It**: Write JavaScript/Python, add npm packages, or use the visual interface
|
||||
- **AI-Native Platform**: Build AI agent workflows based on LangChain with your own data and models
|
||||
- **Full Control**: Self-host with our fair-code license or use our [cloud offering](https://app.n8n.cloud/login)
|
||||
- **Enterprise-Ready**: Advanced permissions, SSO, and air-gapped deployments
|
||||
- **Active Community**: 400+ integrations and 900+ ready-to-use [templates](https://n8n.io/workflows)
|
||||
|
||||
## Contents
|
||||
|
||||
- [n8n - Workflow automation tool](#n8n---workflow-automation-tool)
|
||||
- [Key Capabilities](#key-capabilities)
|
||||
- [Contents](#contents)
|
||||
- [Demo](#demo)
|
||||
- [Available integrations](#available-integrations)
|
||||
- [Documentation](#documentation)
|
||||
- [Start n8n in Docker](#start-n8n-in-docker)
|
||||
- [Use with PostgreSQL](#use-with-postgresql)
|
||||
- [Passing sensitive data using files](#passing-sensitive-data-using-files)
|
||||
- [Example server setups](#example-server-setups)
|
||||
- [Updating](#updating)
|
||||
- [Pull latest (stable) version](#pull-latest-stable-version)
|
||||
- [Pull specific version](#pull-specific-version)
|
||||
- [Pull next (unstable) version](#pull-next-unstable-version)
|
||||
- [Updating with Docker Compose](#updating-with-docker-compose)
|
||||
- [Setting Timezone](#setting-the-timezone)
|
||||
- [Build Docker-Image](#build-docker-image)
|
||||
- [What does n8n mean and how do you pronounce it?](#what-does-n8n-mean-and-how-do-you-pronounce-it)
|
||||
- [Support](#support)
|
||||
- [Jobs](#jobs)
|
||||
- [License](#license)
|
||||
|
||||
## Demo
|
||||
|
||||
This [:tv: short video (< 4 min)](https://www.youtube.com/watch?v=RpjQTGKm-ok) goes over key concepts of creating workflows in n8n.
|
||||
|
||||
## Available integrations
|
||||
|
||||
n8n has 200+ different nodes to automate workflows. A full list can be found at [https://n8n.io/integrations](https://n8n.io/integrations).
|
||||
|
||||
## Documentation
|
||||
|
||||
The official n8n documentation can be found at [https://docs.n8n.io](https://docs.n8n.io).
|
||||
|
||||
Additional information and example workflows are available on the website at [https://n8n.io](https://n8n.io).
|
||||
|
||||
## Start n8n in Docker
|
||||
|
||||
In the terminal, enter the following:
|
||||
|
||||
```bash
|
||||
docker volume create n8n_data
|
||||
|
||||
docker run -it --rm \
|
||||
--name n8n \
|
||||
-p 5678:5678 \
|
||||
-v n8n_data:/home/node/.n8n \
|
||||
docker.n8n.io/n8nio/n8n
|
||||
```
|
||||
|
||||
This command will download the required n8n image and start your container.
|
||||
You can then access n8n by opening:
|
||||
[http://localhost:5678](http://localhost:5678)
|
||||
|
||||
To save your work between container restarts, it also mounts a docker volume, `n8n_data`. The workflow data gets saved in an SQLite database in the user folder (`/home/node/.n8n`). This folder also contains important data like the webhook URL and the encryption key used for securing credentials.
|
||||
|
||||
If this data can't be found at startup n8n automatically creates a new key and any existing credentials can no longer be decrypted.
|
||||
|
||||
## Use with PostgreSQL
|
||||
|
||||
By default, n8n uses SQLite to save credentials, past executions and workflows. However, n8n also supports using PostgreSQL.
|
||||
|
||||
> **WARNING**: Even when using a different database, it is still important to
|
||||
persist the `/home/node/.n8n` folder, which also contains essential n8n
|
||||
user data including the encryption key for the credentials.
|
||||
|
||||
In the following commands, replace the placeholders (depicted within angled brackets, e.g. `<POSTGRES_USER>`) with the actual data:
|
||||
|
||||
```bash
|
||||
docker volume create n8n_data
|
||||
|
||||
docker run -it --rm \
|
||||
--name n8n \
|
||||
-p 5678:5678 \
|
||||
-e DB_TYPE=postgresdb \
|
||||
-e DB_POSTGRESDB_DATABASE=<POSTGRES_DATABASE> \
|
||||
-e DB_POSTGRESDB_HOST=<POSTGRES_HOST> \
|
||||
-e DB_POSTGRESDB_PORT=<POSTGRES_PORT> \
|
||||
-e DB_POSTGRESDB_USER=<POSTGRES_USER> \
|
||||
-e DB_POSTGRESDB_SCHEMA=<POSTGRES_SCHEMA> \
|
||||
-e DB_POSTGRESDB_PASSWORD=<POSTGRES_PASSWORD> \
|
||||
-v n8n_data:/home/node/.n8n \
|
||||
docker.n8n.io/n8nio/n8n
|
||||
```
|
||||
|
||||
A full working setup with docker-compose can be found [here](https://github.com/n8n-io/n8n-hosting/blob/main/docker-compose/withPostgres/README.md).
|
||||
|
||||
## Passing sensitive data using files
|
||||
|
||||
To avoid passing sensitive information via environment variables, "\_FILE" may be appended to some environment variable names. n8n will then load the data from a file with the given name. This makes it possible to load data easily from Docker and Kubernetes secrets.
|
||||
|
||||
The following environment variables support file input:
|
||||
|
||||
- DB_POSTGRESDB_DATABASE_FILE
|
||||
- DB_POSTGRESDB_HOST_FILE
|
||||
- DB_POSTGRESDB_PASSWORD_FILE
|
||||
- DB_POSTGRESDB_PORT_FILE
|
||||
- DB_POSTGRESDB_USER_FILE
|
||||
- DB_POSTGRESDB_SCHEMA_FILE
|
||||
|
||||
## Example server setups
|
||||
|
||||
Example server setups for a range of cloud providers and scenarios can be found in the [Server Setup documentation](https://docs.n8n.io/hosting/installation/server-setups/).
|
||||
|
||||
## Updating
|
||||
|
||||
Before you upgrade to the latest version make sure to check here if there are any breaking changes which may affect you: [Breaking Changes](https://github.com/n8n-io/n8n/blob/master/packages/cli/BREAKING-CHANGES.md)
|
||||
|
||||
From your Docker Desktop, navigate to the Images tab and select Pull from the context menu to download the latest n8n image.
|
||||
|
||||
You can also use the command line to pull the latest, or a specific version:
|
||||
|
||||
### Pull latest (stable) version
|
||||
|
||||
```bash
|
||||
docker pull docker.n8n.io/n8nio/n8n
|
||||
```
|
||||
|
||||
### Pull specific version
|
||||
|
||||
```bash
|
||||
docker pull docker.n8n.io/n8nio/n8n:0.220.1
|
||||
```
|
||||
|
||||
### Pull next (unstable) version
|
||||
|
||||
```bash
|
||||
docker pull docker.n8n.io/n8nio/n8n:next
|
||||
```
|
||||
|
||||
Stop the container and start it again:
|
||||
|
||||
1. Get the container ID:
|
||||
|
||||
```bash
|
||||
docker ps -a
|
||||
```
|
||||
|
||||
2. Stop the container with ID container_id:
|
||||
|
||||
```bash
|
||||
docker stop [container_id]
|
||||
```
|
||||
|
||||
3. Remove the container (this does not remove your user data) with ID container_id:
|
||||
|
||||
```bash
|
||||
docker rm [container_id]
|
||||
```
|
||||
|
||||
4. Start the new container:
|
||||
|
||||
```bash
|
||||
docker run --name=[container_name] [options] -d docker.n8n.io/n8nio/n8n
|
||||
```
|
||||
|
||||
### Updating with Docker Compose
|
||||
|
||||
If you run n8n using a Docker Compose file, follow these steps to update n8n:
|
||||
|
||||
```bash
|
||||
# Pull latest version
|
||||
docker compose pull
|
||||
|
||||
# Stop and remove older version
|
||||
docker compose down
|
||||
|
||||
# Start the container
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Setting the timezone
|
||||
|
||||
To specify the timezone n8n should use, the environment variable `GENERIC_TIMEZONE` can
|
||||
be set. One example where this variable has an effect is the Schedule node.
|
||||
|
||||
The system's timezone can be set separately with the environment variable `TZ`.
|
||||
This controls the output of certain scripts and commands such as `$ date`.
|
||||
|
||||
For example, to use the same timezone for both:
|
||||
|
||||
```bash
|
||||
docker run -it --rm \
|
||||
--name n8n \
|
||||
-p 5678:5678 \
|
||||
-e GENERIC_TIMEZONE="Europe/Berlin" \
|
||||
-e TZ="Europe/Berlin" \
|
||||
docker.n8n.io/n8nio/n8n
|
||||
```
|
||||
|
||||
For more information on configuration and environment variables, please see the [n8n documentation](https://docs.n8n.io/hosting/configuration/environment-variables/).
|
||||
|
||||
|
||||
Here's the refined version with good Markdown formatting, ready for your `README`:
|
||||
|
||||
## Build Docker Image
|
||||
|
||||
**Important Note for Releases 1.101.0 and Later:**
|
||||
Building the n8n Docker image now requires a pre-compiled n8n application.
|
||||
|
||||
### Recommended Build Process:
|
||||
|
||||
For the simplest approach that handles both n8n compilation and Docker image creation, run from the root directory:
|
||||
|
||||
```bash
|
||||
pnpm build:docker
|
||||
```
|
||||
|
||||
### Alternative Builders:
|
||||
|
||||
If you are using a different build system that requires a separate build context, first compile the n8n application:
|
||||
|
||||
```bash
|
||||
pnpm run build:deploy
|
||||
```
|
||||
|
||||
Then, ensure your builder's context includes the `compiled` directory generated by this command.
|
||||
|
||||
|
||||
## What does n8n mean and how do you pronounce it?
|
||||
|
||||
**Short answer:** It means "nodemation" and it is pronounced as n-eight-n.
|
||||
|
||||
**Long answer:** I get that question quite often (more often than I expected) so I decided it is probably best to answer it here. While looking for a good name for the project with a free domain I realized very quickly that all the good ones I could think of were already taken. So, in the end, I chose nodemation. "node-" in the sense that it uses a Node-View and that it uses Node.js and "-mation" for "automation" which is what the project is supposed to help with.
|
||||
However, I did not like how long the name was and I could not imagine writing something that long every time in the CLI. That is when I then ended up on "n8n". Sure it does not work perfectly but neither does it for Kubernetes (k8s) and I did not hear anybody complain there. So I guess it should be ok.
|
||||
|
||||
## Support
|
||||
|
||||
If you need more help with n8n, you can ask for support in the [n8n community forum](https://community.n8n.io). This is the best source of answers, as both the n8n support team and community members can help.
|
||||
|
||||
## Jobs
|
||||
|
||||
If you are interested in working for n8n and so shape the future of the project check out our [job posts](https://jobs.ashbyhq.com/n8n).
|
||||
|
||||
## License
|
||||
|
||||
You can find the license information [here](https://github.com/n8n-io/n8n/blob/master/README.md#license).
|
||||
15
docker/images/n8n/docker-entrypoint.sh
Normal file
15
docker/images/n8n/docker-entrypoint.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
if [ -d /opt/custom-certificates ]; then
|
||||
echo "Trusting custom certificates from /opt/custom-certificates."
|
||||
export NODE_OPTIONS="--use-openssl-ca $NODE_OPTIONS"
|
||||
export SSL_CERT_DIR=/opt/custom-certificates
|
||||
c_rehash /opt/custom-certificates
|
||||
fi
|
||||
|
||||
if [ "$#" -gt 0 ]; then
|
||||
# Got started with arguments
|
||||
exec n8n "$@"
|
||||
else
|
||||
# Got started without arguments
|
||||
exec n8n
|
||||
fi
|
||||
154
docker/images/runners/Dockerfile
Normal file
154
docker/images/runners/Dockerfile
Normal file
@@ -0,0 +1,154 @@
|
||||
ARG NODE_VERSION=24.13.1
|
||||
ARG PYTHON_VERSION=3.13
|
||||
|
||||
# ==============================================================================
|
||||
# STAGE 1: JavaScript runner (@n8n/task-runner) artifact from CI
|
||||
# ==============================================================================
|
||||
FROM node:${NODE_VERSION}-alpine AS javascript-runner-builder
|
||||
COPY ./dist/task-runner-javascript /app/task-runner-javascript
|
||||
|
||||
WORKDIR /app/task-runner-javascript
|
||||
|
||||
RUN corepack enable pnpm
|
||||
|
||||
# Remove `catalog` and `workspace` references from package.json to allow `pnpm add` in extended images
|
||||
RUN node -e "const pkg = require('./package.json'); \
|
||||
Object.keys(pkg.dependencies || {}).forEach(k => { \
|
||||
const val = pkg.dependencies[k]; \
|
||||
if (val === 'catalog:' || val.startsWith('catalog:') || val.startsWith('workspace:')) \
|
||||
delete pkg.dependencies[k]; \
|
||||
}); \
|
||||
Object.keys(pkg.devDependencies || {}).forEach(k => { \
|
||||
const val = pkg.devDependencies[k]; \
|
||||
if (val === 'catalog:' || val.startsWith('catalog:') || val.startsWith('workspace:')) \
|
||||
delete pkg.devDependencies[k]; \
|
||||
}); \
|
||||
delete pkg.devDependencies; \
|
||||
require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2));"
|
||||
|
||||
# Install moment (special case for backwards compatibility)
|
||||
RUN rm -f node_modules/.modules.yaml && \
|
||||
pnpm add moment@2.30.1 --prod --no-lockfile
|
||||
|
||||
# ==============================================================================
|
||||
# STAGE 2: Python runner build (@n8n/task-runner-python) with uv
|
||||
# Produces a relocatable venv tied to the python version used
|
||||
# ==============================================================================
|
||||
FROM python:${PYTHON_VERSION}-alpine AS python-runner-builder
|
||||
ARG TARGETPLATFORM
|
||||
ARG UV_VERSION=0.8.14
|
||||
|
||||
RUN set -e; \
|
||||
case "$TARGETPLATFORM" in \
|
||||
"linux/amd64") UV_ARCH="x86_64-unknown-linux-musl" ;; \
|
||||
"linux/arm64") UV_ARCH="aarch64-unknown-linux-musl" ;; \
|
||||
*) echo "Unsupported platform: $TARGETPLATFORM" >&2; exit 1 ;; \
|
||||
esac; \
|
||||
mkdir -p /tmp/uv && cd /tmp/uv; \
|
||||
wget -q "https://github.com/astral-sh/uv/releases/download/${UV_VERSION}/uv-${UV_ARCH}.tar.gz"; \
|
||||
wget -q "https://github.com/astral-sh/uv/releases/download/${UV_VERSION}/uv-${UV_ARCH}.tar.gz.sha256"; \
|
||||
sha256sum -c "uv-${UV_ARCH}.tar.gz.sha256"; \
|
||||
tar -xzf "uv-${UV_ARCH}.tar.gz"; \
|
||||
install -m 0755 "uv-${UV_ARCH}/uv" /usr/local/bin/uv; \
|
||||
cd / && rm -rf /tmp/uv
|
||||
|
||||
WORKDIR /app/task-runner-python
|
||||
|
||||
COPY packages/@n8n/task-runner-python/pyproject.toml \
|
||||
packages/@n8n/task-runner-python/uv.lock** \
|
||||
packages/@n8n/task-runner-python/.python-version** \
|
||||
./
|
||||
|
||||
RUN uv venv
|
||||
RUN uv sync \
|
||||
--frozen \
|
||||
--no-editable \
|
||||
--no-install-project \
|
||||
--no-dev \
|
||||
--all-extras
|
||||
|
||||
COPY packages/@n8n/task-runner-python/ ./
|
||||
RUN uv sync \
|
||||
--frozen \
|
||||
--no-dev \
|
||||
--all-extras \
|
||||
--no-editable
|
||||
|
||||
# Install the python runner package itself into site packages. We can remove the src directory then
|
||||
RUN uv pip install . && rm -rf /app/task-runner-python/src
|
||||
|
||||
# ==============================================================================
|
||||
# STAGE 3: Task Runner Launcher download
|
||||
# ==============================================================================
|
||||
FROM alpine:3.22 AS launcher-downloader
|
||||
ARG TARGETPLATFORM
|
||||
ARG LAUNCHER_VERSION=1.4.3
|
||||
|
||||
RUN set -e; \
|
||||
case "$TARGETPLATFORM" in \
|
||||
"linux/amd64") ARCH_NAME="amd64" ;; \
|
||||
"linux/arm64") ARCH_NAME="arm64" ;; \
|
||||
*) echo "Unsupported platform: $TARGETPLATFORM" && exit 1 ;; \
|
||||
esac; \
|
||||
mkdir /launcher-temp && cd /launcher-temp; \
|
||||
wget -q "https://github.com/n8n-io/task-runner-launcher/releases/download/${LAUNCHER_VERSION}/task-runner-launcher-${LAUNCHER_VERSION}-linux-${ARCH_NAME}.tar.gz"; \
|
||||
wget -q "https://github.com/n8n-io/task-runner-launcher/releases/download/${LAUNCHER_VERSION}/task-runner-launcher-${LAUNCHER_VERSION}-linux-${ARCH_NAME}.tar.gz.sha256"; \
|
||||
echo "$(cat task-runner-launcher-${LAUNCHER_VERSION}-linux-${ARCH_NAME}.tar.gz.sha256) task-runner-launcher-${LAUNCHER_VERSION}-linux-${ARCH_NAME}.tar.gz" > checksum.sha256; \
|
||||
sha256sum -c checksum.sha256; \
|
||||
mkdir -p /launcher-bin; \
|
||||
tar xzf task-runner-launcher-${LAUNCHER_VERSION}-linux-${ARCH_NAME}.tar.gz -C /launcher-bin; \
|
||||
cd / && rm -rf /launcher-temp
|
||||
|
||||
# ==============================================================================
|
||||
# STAGE 4: Node alpine base for JS task runner
|
||||
# ==============================================================================
|
||||
FROM node:${NODE_VERSION}-alpine AS node-alpine
|
||||
|
||||
# ==============================================================================
|
||||
# STAGE 5: Runtime
|
||||
# ==============================================================================
|
||||
FROM python:${PYTHON_VERSION}-alpine AS runtime
|
||||
ARG N8N_VERSION=snapshot
|
||||
ARG N8N_RELEASE_TYPE=dev
|
||||
|
||||
ENV NODE_ENV=production \
|
||||
N8N_RELEASE_TYPE=${N8N_RELEASE_TYPE} \
|
||||
SHELL=/bin/sh
|
||||
|
||||
# Bring `uv` over from python-runner-builder, to make the image easier to extend
|
||||
COPY --from=python-runner-builder /usr/local/bin/uv /usr/local/bin/uv
|
||||
|
||||
# Bring node over from node-alpine
|
||||
COPY --from=node-alpine /usr/local/bin/node /usr/local/bin/node
|
||||
|
||||
# libstdc++ is required by Node
|
||||
# libc6-compat is required by task-runner-launcher
|
||||
RUN apk add --no-cache ca-certificates tini libstdc++ libc6-compat && \
|
||||
apk del apk-tools
|
||||
|
||||
# Bring corepack and pnpm over, to make the image easier to extend
|
||||
COPY --from=node-alpine /usr/local/lib/node_modules/corepack /usr/local/lib/node_modules/corepack
|
||||
RUN ln -s ../lib/node_modules/corepack/dist/corepack.js /usr/local/bin/corepack && \
|
||||
ln -s ../lib/node_modules/corepack/dist/pnpm.js /usr/local/bin/pnpm
|
||||
|
||||
RUN addgroup -g 1000 -S runner \
|
||||
&& adduser -u 1000 -S -G runner -h /home/runner -D runner
|
||||
|
||||
WORKDIR /home/runner
|
||||
|
||||
COPY --from=javascript-runner-builder --chown=root:root /app/task-runner-javascript /opt/runners/task-runner-javascript
|
||||
COPY --from=python-runner-builder --chown=root:root /app/task-runner-python /opt/runners/task-runner-python
|
||||
COPY --from=launcher-downloader /launcher-bin/* /usr/local/bin/
|
||||
COPY --chown=root:root docker/images/runners/n8n-task-runners.json /etc/n8n-task-runners.json
|
||||
|
||||
USER runner
|
||||
|
||||
EXPOSE 5680/tcp
|
||||
ENTRYPOINT ["tini", "--", "/usr/local/bin/task-runner-launcher"]
|
||||
CMD ["javascript", "python"]
|
||||
|
||||
LABEL org.opencontainers.image.title="n8n task runners" \
|
||||
org.opencontainers.image.description="Sidecar image providing n8n task runners for JavaScript and Python code execution" \
|
||||
org.opencontainers.image.source="https://github.com/n8n-io/n8n" \
|
||||
org.opencontainers.image.url="https://n8n.io" \
|
||||
org.opencontainers.image.version="${N8N_VERSION}"
|
||||
198
docker/images/runners/Dockerfile.distroless
Normal file
198
docker/images/runners/Dockerfile.distroless
Normal file
@@ -0,0 +1,198 @@
|
||||
# ==============================================================================
|
||||
# DISTROLESS RUNNERS IMAGE
|
||||
# ==============================================================================
|
||||
# This is a distroless variant of docker/images/runners/Dockerfile, intended
|
||||
# for cloud deployments. It removes all shell, package managers, and system
|
||||
# utilities for security hardening.
|
||||
#
|
||||
# Key differences:
|
||||
# - Uses Debian-based builders (glibc instead of musl)
|
||||
# - Final image is Google's distroless/cc-debian12
|
||||
# - Extra runtime-prep stage to organize filesystem
|
||||
# - Uses distroless nonroot user (UID 65532)
|
||||
# ==============================================================================
|
||||
|
||||
ARG NODE_VERSION=24.13.1
|
||||
ARG PYTHON_VERSION=3.13
|
||||
|
||||
|
||||
# ==============================================================================
|
||||
# STAGE 1: JavaScript runner (@n8n/task-runner) artifact from CI
|
||||
# ==============================================================================
|
||||
FROM node:${NODE_VERSION}-bookworm-slim AS javascript-runner-builder
|
||||
COPY ./dist/task-runner-javascript /app/task-runner-javascript
|
||||
|
||||
WORKDIR /app/task-runner-javascript
|
||||
|
||||
RUN corepack enable pnpm
|
||||
|
||||
# Remove `catalog` and `workspace` references from package.json to allow `pnpm add`
|
||||
RUN node -e "const pkg = require('./package.json'); \
|
||||
Object.keys(pkg.dependencies || {}).forEach(k => { \
|
||||
const val = pkg.dependencies[k]; \
|
||||
if (val === 'catalog:' || val.startsWith('catalog:') || val.startsWith('workspace:')) \
|
||||
delete pkg.dependencies[k]; \
|
||||
}); \
|
||||
Object.keys(pkg.devDependencies || {}).forEach(k => { \
|
||||
const val = pkg.devDependencies[k]; \
|
||||
if (val === 'catalog:' || val.startsWith('catalog:') || val.startsWith('workspace:')) \
|
||||
delete pkg.devDependencies[k]; \
|
||||
}); \
|
||||
delete pkg.devDependencies; \
|
||||
require('fs').writeFileSync('./package.json', JSON.stringify(pkg, null, 2));"
|
||||
|
||||
# Install moment by default (special case for n8n cloud)
|
||||
RUN rm -f node_modules/.modules.yaml && \
|
||||
pnpm add moment@2.30.1 --prod --no-lockfile
|
||||
|
||||
# ==============================================================================
|
||||
# STAGE 2: Python runner build (@n8n/task-runner-python) with uv
|
||||
# Produces a relocatable venv tied to the python version used
|
||||
# ==============================================================================
|
||||
FROM python:${PYTHON_VERSION}-slim-bookworm AS python-runner-builder
|
||||
ARG TARGETPLATFORM
|
||||
ARG UV_VERSION=0.8.14
|
||||
|
||||
RUN set -e; \
|
||||
apt-get update && apt-get install -y --no-install-recommends wget ca-certificates && \
|
||||
case "$TARGETPLATFORM" in \
|
||||
"linux/amd64") UV_ARCH="x86_64-unknown-linux-gnu" ;; \
|
||||
"linux/arm64") UV_ARCH="aarch64-unknown-linux-gnu" ;; \
|
||||
*) echo "Unsupported platform: $TARGETPLATFORM" >&2; exit 1 ;; \
|
||||
esac; \
|
||||
mkdir -p /tmp/uv && cd /tmp/uv; \
|
||||
wget -q "https://github.com/astral-sh/uv/releases/download/${UV_VERSION}/uv-${UV_ARCH}.tar.gz"; \
|
||||
wget -q "https://github.com/astral-sh/uv/releases/download/${UV_VERSION}/uv-${UV_ARCH}.tar.gz.sha256"; \
|
||||
sha256sum -c "uv-${UV_ARCH}.tar.gz.sha256"; \
|
||||
tar -xzf "uv-${UV_ARCH}.tar.gz"; \
|
||||
install -m 0755 "uv-${UV_ARCH}/uv" /usr/local/bin/uv; \
|
||||
cd / && rm -rf /tmp/uv && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app/task-runner-python
|
||||
|
||||
COPY packages/@n8n/task-runner-python/pyproject.toml \
|
||||
packages/@n8n/task-runner-python/uv.lock** \
|
||||
packages/@n8n/task-runner-python/.python-version** \
|
||||
./
|
||||
|
||||
RUN uv venv
|
||||
RUN uv sync \
|
||||
--frozen \
|
||||
--no-editable \
|
||||
--no-install-project \
|
||||
--no-dev \
|
||||
--all-extras
|
||||
|
||||
COPY packages/@n8n/task-runner-python/ ./
|
||||
RUN uv sync \
|
||||
--frozen \
|
||||
--no-dev \
|
||||
--all-extras \
|
||||
--no-editable
|
||||
|
||||
# Install the python runner package itself into site packages. We can remove the src directory then
|
||||
RUN uv pip install . && rm -rf /app/task-runner-python/src
|
||||
|
||||
# ==============================================================================
|
||||
# STAGE 3: Task Runner Launcher download
|
||||
# ==============================================================================
|
||||
FROM debian:bookworm-slim AS launcher-downloader
|
||||
ARG TARGETPLATFORM
|
||||
ARG LAUNCHER_VERSION=1.4.3
|
||||
|
||||
RUN set -e; \
|
||||
apt-get update && apt-get install -y --no-install-recommends wget ca-certificates && \
|
||||
case "$TARGETPLATFORM" in \
|
||||
"linux/amd64") ARCH_NAME="amd64" ;; \
|
||||
"linux/arm64") ARCH_NAME="arm64" ;; \
|
||||
*) echo "Unsupported platform: $TARGETPLATFORM" && exit 1 ;; \
|
||||
esac; \
|
||||
mkdir /launcher-temp && cd /launcher-temp; \
|
||||
wget -q "https://github.com/n8n-io/task-runner-launcher/releases/download/${LAUNCHER_VERSION}/task-runner-launcher-${LAUNCHER_VERSION}-linux-${ARCH_NAME}.tar.gz"; \
|
||||
wget -q "https://github.com/n8n-io/task-runner-launcher/releases/download/${LAUNCHER_VERSION}/task-runner-launcher-${LAUNCHER_VERSION}-linux-${ARCH_NAME}.tar.gz.sha256"; \
|
||||
echo "$(cat task-runner-launcher-${LAUNCHER_VERSION}-linux-${ARCH_NAME}.tar.gz.sha256) task-runner-launcher-${LAUNCHER_VERSION}-linux-${ARCH_NAME}.tar.gz" > checksum.sha256; \
|
||||
sha256sum -c checksum.sha256; \
|
||||
mkdir -p /launcher-bin; \
|
||||
tar xzf task-runner-launcher-${LAUNCHER_VERSION}-linux-${ARCH_NAME}.tar.gz -C /launcher-bin; \
|
||||
cd / && rm -rf /launcher-temp && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# ==============================================================================
|
||||
# STAGE 4: Node Debian base for JS task runner
|
||||
# ==============================================================================
|
||||
FROM node:${NODE_VERSION}-bookworm-slim AS node-debian
|
||||
|
||||
# ==============================================================================
|
||||
# STAGE 5: Runtime preparation
|
||||
# ==============================================================================
|
||||
# Prepare a clean filesystem structure with all necessary files before
|
||||
# copying to the distroless base. This stage runs with a shell so we can
|
||||
# create symlinks and organize files properly.
|
||||
# ==============================================================================
|
||||
FROM debian:bookworm-slim AS runtime-prep
|
||||
|
||||
# Copy Python runtime
|
||||
COPY --from=python-runner-builder /usr/local/bin/python3.13 /runtime/usr/local/bin/python3.13
|
||||
COPY --from=python-runner-builder /usr/local/lib/python3.13 /runtime/usr/local/lib/python3.13
|
||||
COPY --from=python-runner-builder /usr/local/lib/libpython3.13.so* /runtime/usr/local/lib/
|
||||
|
||||
# Copy Python dependencies (architecture-specific directories)
|
||||
# The /* glob will match x86_64-linux-gnu or aarch64-linux-gnu
|
||||
COPY --from=python-runner-builder /lib/*-linux-gnu* /runtime/lib/
|
||||
COPY --from=python-runner-builder /usr/lib/*-linux-gnu* /runtime/usr/lib/
|
||||
|
||||
# Copy Node.js runtime
|
||||
COPY --from=node-debian /usr/local/bin/node /runtime/usr/local/bin/node
|
||||
|
||||
# Copy task runners
|
||||
COPY --from=javascript-runner-builder /app/task-runner-javascript /runtime/opt/runners/task-runner-javascript
|
||||
COPY --from=python-runner-builder /app/task-runner-python /runtime/opt/runners/task-runner-python
|
||||
|
||||
# Copy launcher
|
||||
COPY --from=launcher-downloader /launcher-bin/* /runtime/usr/local/bin/
|
||||
|
||||
# Copy configuration
|
||||
COPY docker/images/runners/n8n-task-runners.json /runtime/etc/n8n-task-runners.json
|
||||
|
||||
# Create necessary directories with proper permissions
|
||||
RUN mkdir -p /runtime/home/runner && \
|
||||
chmod 755 /runtime/home/runner && \
|
||||
chmod +x /runtime/usr/local/bin/*
|
||||
|
||||
# ==============================================================================
|
||||
# STAGE 6: Distroless Runtime
|
||||
# ==============================================================================
|
||||
# Uses Google's distroless/cc-debian12 which provides:
|
||||
# - glibc, libgcc, libstdc++
|
||||
# - CA certificates
|
||||
# - Timezone data
|
||||
# - nonroot user (UID 65532)
|
||||
# - NO shell, NO package manager, NO system utilities
|
||||
# ==============================================================================
|
||||
FROM gcr.io/distroless/cc-debian12:latest AS runtime
|
||||
ARG N8N_VERSION=snapshot
|
||||
ARG N8N_RELEASE_TYPE=dev
|
||||
|
||||
ENV NODE_ENV=production \
|
||||
N8N_RELEASE_TYPE=${N8N_RELEASE_TYPE} \
|
||||
HOME=/home/runner
|
||||
|
||||
# Copy everything from the prepared runtime filesystem
|
||||
COPY --from=runtime-prep --chown=root:root /runtime/ /
|
||||
|
||||
WORKDIR /home/runner
|
||||
|
||||
# Switch to nonroot user (UID 65532)
|
||||
USER 65532:65532
|
||||
|
||||
EXPOSE 5680/tcp
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/task-runner-launcher"]
|
||||
CMD ["javascript", "python"]
|
||||
|
||||
LABEL org.opencontainers.image.title="n8n task runners (distroless)" \
|
||||
org.opencontainers.image.description="Distroless sidecar image providing n8n task runners for JavaScript and Python code execution" \
|
||||
org.opencontainers.image.source="https://github.com/n8n-io/n8n" \
|
||||
org.opencontainers.image.url="https://n8n.io" \
|
||||
org.opencontainers.image.version="${N8N_VERSION}"
|
||||
56
docker/images/runners/README.md
Normal file
56
docker/images/runners/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# n8n - Task runners (`n8nio/runners`) - (PREVIEW)
|
||||
|
||||
`n8nio/runners` image includes [JavaScript runner](https://github.com/n8n-io/n8n/tree/master/packages/%40n8n/task-runner),
|
||||
[Python runner](https://github.com/n8n-io/n8n/tree/master/packages/%40n8n/task-runner-python) and
|
||||
[Task runner launcher](https://github.com/n8n-io/task-runner-launcher) that connects to a Task Broker
|
||||
running on the main n8n instance when running in `external` mode. This image is to be launched as a sidecar
|
||||
container to the main n8n container.
|
||||
|
||||
[Task runners](https://docs.n8n.io/hosting/configuration/task-runners/) are used to execute user-provided code
|
||||
in the [Code Node](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.code/), isolated from the n8n instance.
|
||||
|
||||
For official documentation, please see [here](https://docs.n8n.io/hosting/configuration/task-runners/).
|
||||
|
||||
For a distroless variant of this image, see [here](./Dockerfile.distroless).
|
||||
|
||||
For development purposes only, see below.
|
||||
|
||||
## Testing locally
|
||||
|
||||
### 1) Make a production build of n8n
|
||||
|
||||
```
|
||||
pnpm run build:n8n
|
||||
```
|
||||
|
||||
### 2) Build the task runners image
|
||||
|
||||
```
|
||||
docker buildx build \
|
||||
-f docker/images/runners/Dockerfile \
|
||||
-t n8nio/runners \
|
||||
.
|
||||
```
|
||||
|
||||
### 3) Start n8n on your host machine with Task Broker enabled
|
||||
|
||||
```
|
||||
N8N_RUNNERS_MODE=external \
|
||||
N8N_RUNNERS_AUTH_TOKEN=test \
|
||||
N8N_LOG_LEVEL=debug \
|
||||
pnpm start
|
||||
```
|
||||
|
||||
### 4) Start the task runner container
|
||||
|
||||
```
|
||||
docker run --rm -it \
|
||||
-e N8N_RUNNERS_AUTH_TOKEN=test \
|
||||
-e N8N_RUNNERS_LAUNCHER_LOG_LEVEL=debug \
|
||||
-e N8N_RUNNERS_TASK_BROKER_URI=http://host.docker.internal:5679 \
|
||||
-p 5680:5680 \
|
||||
n8nio/runners
|
||||
```
|
||||
|
||||
If you need to add extra dependencies (custom image), follow [these instructions](https://docs.n8n.io/hosting/configuration/task-runners/#adding-extra-dependencies).
|
||||
|
||||
55
docker/images/runners/n8n-task-runners.json
Normal file
55
docker/images/runners/n8n-task-runners.json
Normal file
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"task-runners": [
|
||||
{
|
||||
"runner-type": "javascript",
|
||||
"workdir": "/home/runner",
|
||||
"command": "/usr/local/bin/node",
|
||||
"args": [
|
||||
"--disallow-code-generation-from-strings",
|
||||
"--disable-proto=delete",
|
||||
"/opt/runners/task-runner-javascript/dist/start.js"
|
||||
],
|
||||
"health-check-server-port": "5681",
|
||||
"allowed-env": [
|
||||
"PATH",
|
||||
"GENERIC_TIMEZONE",
|
||||
"NODE_OPTIONS",
|
||||
"N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT",
|
||||
"N8N_RUNNERS_TASK_TIMEOUT",
|
||||
"N8N_RUNNERS_MAX_CONCURRENCY",
|
||||
"N8N_SENTRY_DSN",
|
||||
"N8N_VERSION",
|
||||
"ENVIRONMENT",
|
||||
"DEPLOYMENT_NAME",
|
||||
"HOME"
|
||||
],
|
||||
"env-overrides": {
|
||||
"NODE_FUNCTION_ALLOW_BUILTIN": "crypto",
|
||||
"NODE_FUNCTION_ALLOW_EXTERNAL": "moment",
|
||||
"N8N_RUNNERS_HEALTH_CHECK_SERVER_HOST": "0.0.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"runner-type": "python",
|
||||
"workdir": "/home/runner",
|
||||
"command": "/opt/runners/task-runner-python/.venv/bin/python",
|
||||
"args": ["-I", "-B", "-X", "disable_remote_debug", "-m", "src.main"],
|
||||
"health-check-server-port": "5682",
|
||||
"allowed-env": [
|
||||
"PATH",
|
||||
"N8N_RUNNERS_LAUNCHER_LOG_LEVEL",
|
||||
"N8N_RUNNERS_AUTO_SHUTDOWN_TIMEOUT",
|
||||
"N8N_RUNNERS_TASK_TIMEOUT",
|
||||
"N8N_RUNNERS_MAX_CONCURRENCY",
|
||||
"N8N_SENTRY_DSN",
|
||||
"N8N_VERSION",
|
||||
"ENVIRONMENT",
|
||||
"DEPLOYMENT_NAME"
|
||||
],
|
||||
"env-overrides": {
|
||||
"N8N_RUNNERS_STDLIB_ALLOW": "",
|
||||
"N8N_RUNNERS_EXTERNAL_ALLOW": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user