├── .docker ├── Dockerfile.alpine ├── Dockerfile.debian ├── docker.sh ├── healthcheck.js ├── known_hosts.sh └── scripts │ ├── entrypoint.sh │ ├── install_devtools.sh │ └── remove_native_gpio.sh ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md ├── no-response.yml └── workflows │ ├── main.yml │ └── pr.yaml ├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docker-custom ├── Dockerfile.custom ├── Dockerfile.debian ├── README.md ├── docker-alpine.sh ├── docker-debian.sh ├── flows.json ├── healthcheck.js ├── known_hosts.sh ├── package.json └── scripts │ ├── entrypoint.sh │ ├── install_devtools.sh │ └── remove_native_gpio.sh ├── flows.json ├── package.json └── volumechanges.md /.docker/Dockerfile.alpine: -------------------------------------------------------------------------------- 1 | ARG NODE_VERSION=20 2 | ARG OS=alpine 3 | 4 | #### Stage BASE ######################################################################################################## 5 | FROM node:${NODE_VERSION}-${OS} AS base 6 | ARG QEMU_ARCH 7 | 8 | # Copy scripts 9 | COPY .docker/scripts/*.sh /tmp/ 10 | COPY .docker/healthcheck.js / 11 | 12 | # Install tools, create Node-RED app and data dir, add user and set rights 13 | RUN set -ex && \ 14 | apk add --no-cache \ 15 | bash \ 16 | tzdata \ 17 | iputils \ 18 | curl \ 19 | nano \ 20 | git \ 21 | openssl \ 22 | openssh-client \ 23 | ca-certificates && \ 24 | mkdir -p /usr/src/node-red /data && \ 25 | deluser --remove-home node && \ 26 | adduser -h /usr/src/node-red -D -H node-red -u 1000 && \ 27 | chown -R node-red:root /data && chmod -R g+rwX /data && \ 28 | chown -R node-red:root /usr/src/node-red && chmod -R g+rwX /usr/src/node-red 29 | # chown -R node-red:node-red /data && \ 30 | # chown -R node-red:node-red /usr/src/node-red 31 | 32 | # Set work directory 33 | WORKDIR /usr/src/node-red 34 | 35 | # Setup SSH known_hosts file 36 | COPY .docker/known_hosts.sh . 37 | RUN ./known_hosts.sh /etc/ssh/ssh_known_hosts && rm /usr/src/node-red/known_hosts.sh 38 | RUN echo "PubkeyAcceptedKeyTypes +ssh-rsa" >> /etc/ssh/ssh_config 39 | 40 | # package.json contains Node-RED NPM module and node dependencies 41 | COPY package.json . 42 | COPY flows.json /data 43 | COPY .docker/scripts/entrypoint.sh . 44 | 45 | #### Stage BUILD ####################################################################################################### 46 | FROM base AS build 47 | 48 | # Install Build tools 49 | RUN apk add --no-cache --virtual buildtools build-base linux-headers udev python3 && \ 50 | npm install --unsafe-perm --no-update-notifier --no-audit --no-fund --only=production && \ 51 | /tmp/remove_native_gpio.sh && \ 52 | cp -R node_modules prod_node_modules 53 | 54 | #### Stage RELEASE ##################################################################################################### 55 | FROM base AS release 56 | ARG BUILD_DATE 57 | ARG BUILD_VERSION 58 | ARG BUILD_REF 59 | ARG NODE_RED_VERSION 60 | ARG ARCH 61 | ARG QEMU_ARCH 62 | ARG TAG_SUFFIX 63 | 64 | LABEL org.label-schema.build-date=${BUILD_DATE} \ 65 | org.label-schema.docker.dockerfile=".docker/Dockerfile.alpine" \ 66 | org.label-schema.license="Apache-2.0" \ 67 | org.label-schema.name="Node-RED" \ 68 | org.label-schema.version=${BUILD_VERSION} \ 69 | org.label-schema.description="Low-code programming for event-driven applications." \ 70 | org.label-schema.url="https://nodered.org" \ 71 | org.label-schema.vcs-ref=${BUILD_REF} \ 72 | org.label-schema.vcs-type="Git" \ 73 | org.label-schema.vcs-url="https://github.com/node-red/node-red-docker" \ 74 | org.opencontainers.image.source="https://github.com/node-red/node-red-docker" \ 75 | org.label-schema.arch=${ARCH} \ 76 | authors="Dave Conway-Jones, Nick O'Leary, James Thomas, Raymond Mouthaan" 77 | 78 | COPY --from=build /usr/src/node-red/prod_node_modules ./node_modules 79 | 80 | # Chown, install devtools & Clean up 81 | RUN chown -R node-red:root /usr/src/node-red && \ 82 | /tmp/install_devtools.sh && \ 83 | rm -r /tmp/* 84 | 85 | RUN npm config set cache /data/.npm --global 86 | 87 | USER node-red 88 | 89 | # Env variables 90 | ENV NODE_RED_VERSION=$NODE_RED_VERSION \ 91 | NODE_PATH=/usr/src/node-red/node_modules:/data/node_modules \ 92 | PATH=/usr/src/node-red/node_modules/.bin:${PATH} \ 93 | FLOWS=flows.json 94 | 95 | # ENV NODE_RED_ENABLE_SAFE_MODE=true # Uncomment to enable safe start mode (flows not running) 96 | # ENV NODE_RED_ENABLE_PROJECTS=true # Uncomment to enable projects option 97 | 98 | # Expose the listening port of node-red 99 | EXPOSE 1880 100 | 101 | # Add a healthcheck (default every 30 secs) 102 | HEALTHCHECK CMD node /healthcheck.js 103 | 104 | ENTRYPOINT ["./entrypoint.sh"] 105 | -------------------------------------------------------------------------------- /.docker/Dockerfile.debian: -------------------------------------------------------------------------------- 1 | ARG NODE_VERSION=20 2 | ARG OS=bullseye-slim 3 | 4 | #### Stage BASE ######################################################################################################## 5 | FROM node:${NODE_VERSION}-${OS} AS base 6 | 7 | # Copy scripts 8 | COPY .docker/scripts/*.sh /tmp/ 9 | COPY .docker/healthcheck.js / 10 | 11 | # Install tools, create Node-RED app and data dir, add user and set rights 12 | RUN set -ex && \ 13 | apt-get update && apt-get install -y \ 14 | bash \ 15 | tzdata \ 16 | curl \ 17 | nano \ 18 | wget \ 19 | git \ 20 | openssl \ 21 | openssh-client \ 22 | ca-certificates \ 23 | iputils-ping && \ 24 | mkdir -p /usr/src/node-red /data && \ 25 | deluser --remove-home node && \ 26 | # adduser --home /usr/src/node-red --disabled-password --no-create-home node-red --uid 1000 && \ 27 | useradd --home-dir /usr/src/node-red --uid 1000 node-red && \ 28 | chown -R node-red:root /data && chmod -R g+rwX /data && \ 29 | chown -R node-red:root /usr/src/node-red && chmod -R g+rwX /usr/src/node-red 30 | # chown -R node-red:node-red /data && \ 31 | # chown -R node-red:node-red /usr/src/node-red 32 | 33 | # Set work directory 34 | WORKDIR /usr/src/node-red 35 | 36 | # Setup SSH known_hosts file 37 | COPY .docker/known_hosts.sh . 38 | RUN ./known_hosts.sh /etc/ssh/ssh_known_hosts && rm /usr/src/node-red/known_hosts.sh 39 | RUN echo "PubkeyAcceptedKeyTypes +ssh-rsa" >> /etc/ssh/ssh_config 40 | 41 | # package.json contains Node-RED NPM module and node dependencies 42 | COPY package.json . 43 | COPY flows.json /data 44 | COPY .docker/scripts/entrypoint.sh . 45 | 46 | #### Stage BUILD ####################################################################################################### 47 | FROM base AS build 48 | 49 | # Install Build tools 50 | RUN apt-get update && apt-get install -y build-essential python && \ 51 | npm install --unsafe-perm --no-update-notifier --no-fund --only=production && \ 52 | npm uninstall node-red-node-gpio && \ 53 | cp -R node_modules prod_node_modules 54 | 55 | #### Stage RELEASE ##################################################################################################### 56 | FROM base AS release 57 | ARG BUILD_DATE 58 | ARG BUILD_VERSION 59 | ARG BUILD_REF 60 | ARG NODE_RED_VERSION 61 | ARG ARCH 62 | ARG TAG_SUFFIX 63 | 64 | LABEL org.label-schema.build-date=${BUILD_DATE} \ 65 | org.label-schema.docker.dockerfile=".docker/Dockerfile.debian" \ 66 | org.label-schema.license="Apache-2.0" \ 67 | org.label-schema.name="Node-RED" \ 68 | org.label-schema.version=${BUILD_VERSION} \ 69 | org.label-schema.description="Low-code programming for event-driven applications." \ 70 | org.label-schema.url="https://nodered.org" \ 71 | org.label-schema.vcs-ref=${BUILD_REF} \ 72 | org.label-schema.vcs-type="Git" \ 73 | org.label-schema.vcs-url="https://github.com/node-red/node-red-docker" \ 74 | org.opencontainers.image.source="https://github.com/node-red/node-red-docker" \ 75 | org.label-schema.arch=${ARCH} \ 76 | authors="Dave Conway-Jones, Nick O'Leary, James Thomas, Raymond Mouthaan" 77 | 78 | COPY --from=build /usr/src/node-red/prod_node_modules ./node_modules 79 | 80 | # Chown, install devtools & Clean up 81 | RUN chown -R node-red:root /usr/src/node-red && \ 82 | apt-get update && apt-get install -y build-essential python-dev python3 && \ 83 | rm -r /tmp/* 84 | 85 | RUN npm config set cache /data/.npm --global 86 | 87 | USER node-red 88 | 89 | # Env variables 90 | ENV NODE_RED_VERSION=$NODE_RED_VERSION \ 91 | NODE_PATH=/usr/src/node-red/node_modules:/data/node_modules \ 92 | PATH=/usr/src/node-red/node_modules/.bin:${PATH} \ 93 | FLOWS=flows.json 94 | 95 | # ENV NODE_RED_ENABLE_SAFE_MODE=true # Uncomment to enable safe start mode (flows not running) 96 | # ENV NODE_RED_ENABLE_PROJECTS=true # Uncomment to enable projects option 97 | 98 | # Expose the listening port of node-red 99 | EXPOSE 1880 100 | 101 | # Add a healthcheck (default every 30 secs) 102 | HEALTHCHECK CMD node /healthcheck.js 103 | 104 | ENTRYPOINT ["./entrypoint.sh"] 105 | -------------------------------------------------------------------------------- /.docker/docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -o errexit 4 | 5 | main() { 6 | # arg 1 holds switch string 7 | # arg 2 holds node version 8 | # arg 3 holds tag suffix 9 | 10 | case $1 in 11 | "prepare") 12 | docker_prepare 13 | ;; 14 | "build") 15 | docker_build 16 | ;; 17 | "test") 18 | docker_test 19 | ;; 20 | "tag") 21 | docker_tag 22 | ;; 23 | "push") 24 | docker_push 25 | ;; 26 | "manifest-list-version") 27 | docker_manifest_list_version "$2" "$3" 28 | ;; 29 | "manifest_list_beta") 30 | docker_manifest_list_beta "$2" "$3" 31 | ;; 32 | "manifest_list_dev") 33 | docker_manifest_list_dev "$2" "$3" 34 | ;; 35 | "manifest_list_test") 36 | docker_manifest_list_test "$2" "$3" 37 | ;; 38 | "manifest_list_latest") 39 | docker_manifest_list_latest "$2" "$3" 40 | ;; 41 | *) 42 | echo "none of above!" 43 | ;; 44 | esac 45 | } 46 | 47 | function docker_prepare() { 48 | # Prepare the machine before any code installation scripts 49 | setup_dependencies 50 | 51 | # Update docker configuration to enable docker manifest command 52 | update_docker_configuration 53 | 54 | # Prepare qemu to build images other then x86_64 on travis 55 | prepare_qemu 56 | } 57 | 58 | function docker_build() { 59 | # Build Docker image 60 | echo "DOCKER BUILD: Build Docker image." 61 | echo "DOCKER BUILD: arch - ${ARCH}." 62 | echo "DOCKER BUILD: node version -> ${NODE_VERSION}." 63 | echo "DOCKER BUILD: os -> ${OS}." 64 | echo "DOCKER BUILD: build version -> ${BUILD_VERSION}." 65 | echo "DOCKER BUILD: node-red version -> ${NODE_RED_VERSION}." 66 | echo "DOCKER BUILD: qemu arch - ${QEMU_ARCH}." 67 | echo "DOCKER BUILD: tag suffix - ${TAG_SUFFIX}." 68 | echo "DOCKER BUILD: docker file - ${DOCKER_FILE}." 69 | 70 | docker build --no-cache \ 71 | --build-arg ARCH=${ARCH} \ 72 | --build-arg NODE_VERSION=${NODE_VERSION} \ 73 | --build-arg OS=${OS} \ 74 | --build-arg BUILD_DATE=$(date +"%Y-%m-%dT%H:%M:%SZ") \ 75 | --build-arg BUILD_VERSION=${BUILD_VERSION} \ 76 | --build-arg BUILD_REF=${TRAVIS_COMMIT} \ 77 | --build-arg NODE_RED_VERSION=v${NODE_RED_VERSION} \ 78 | --build-arg QEMU_ARCH=${QEMU_ARCH} \ 79 | --build-arg TAG_SUFFIX=${TAG_SUFFIX} \ 80 | --file ./.docker/${DOCKER_FILE} \ 81 | --tag ${TARGET}:build . 82 | } 83 | 84 | function docker_test() { 85 | echo "DOCKER TEST: Test Docker image." 86 | echo "DOCKER TEST: testing image -> ${TARGET}:build" 87 | 88 | docker run -d --rm --name=testing ${TARGET}:build 89 | if [ $? -ne 0 ]; then 90 | echo "DOCKER TEST: FAILED - Docker container testing failed to start." 91 | exit 1 92 | else 93 | echo "DOCKER TEST: PASSED - Docker container testing succeeded to start." 94 | fi 95 | } 96 | 97 | function docker_tag() { 98 | echo "DOCKER TAG: Tag Docker image." 99 | 100 | if [[ ${TAG_SUFFIX} == "default" ]]; then export TAG_SUFFIX=""; else export TAG_SUFFIX="-${TAG_SUFFIX}"; fi 101 | 102 | echo "DOCKER TAG: tagging image - ${TARGET}:${BUILD_VERSION}-${NODE_VERSION}${TAG_SUFFIX}-${ARCH}" 103 | docker tag ${TARGET}:build ${TARGET}:${BUILD_VERSION}-${NODE_VERSION}${TAG_SUFFIX}-${ARCH} 104 | } 105 | 106 | function docker_push() { 107 | echo "DOCKER PUSH: Push Docker image." 108 | 109 | if [[ ${TAG_SUFFIX} == "default" ]]; then export TAG_SUFFIX=""; else export TAG_SUFFIX="-${TAG_SUFFIX}"; fi 110 | 111 | echo "DOCKER TAG: pushing image - ${TARGET}:${BUILD_VERSION}-${NODE_VERSION}${TAG_SUFFIX}-${ARCH}" 112 | docker push ${TARGET}:${BUILD_VERSION}-${NODE_VERSION}${TAG_SUFFIX}-${ARCH} 113 | } 114 | 115 | function docker_manifest_list_version() { 116 | 117 | if [[ ${1} == "" ]]; then export NODE_VERSION=""; else export NODE_VERSION="-${1}"; fi 118 | if [[ ${2} == "default" ]]; then export TAG_SUFFIX=""; else export TAG_SUFFIX="-${2}"; fi 119 | 120 | echo "DOCKER MANIFEST: Create and Push docker manifest list - ${TARGET}:${BUILD_VERSION}${NODE_VERSION}${TAG_SUFFIX}." 121 | 122 | docker manifest create ${TARGET}:${BUILD_VERSION}${NODE_VERSION}${TAG_SUFFIX} \ 123 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-amd64 \ 124 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v6 \ 125 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v7 \ 126 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm64v8 \ 127 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-s390x \ 128 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-i386 129 | 130 | docker manifest annotate ${TARGET}:${BUILD_VERSION}${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v6 --os=linux --arch=arm --variant=v6 131 | docker manifest annotate ${TARGET}:${BUILD_VERSION}${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v7 --os=linux --arch=arm --variant=v7 132 | docker manifest annotate ${TARGET}:${BUILD_VERSION}${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm64v8 --os=linux --arch=arm64 --variant=v8 133 | docker manifest annotate ${TARGET}:${BUILD_VERSION}${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-s390x --os=linux --arch=s390x 134 | docker manifest annotate ${TARGET}:${BUILD_VERSION}${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-i386 --os=linux --arch=386 135 | 136 | docker manifest push ${TARGET}:${BUILD_VERSION}${NODE_VERSION}${TAG_SUFFIX} 137 | 138 | docker run --rm mplatform/mquery ${TARGET}:${BUILD_VERSION}${NODE_VERSION}${TAG_SUFFIX} 139 | } 140 | 141 | function docker_manifest_list_beta() { 142 | if [[ ${1} == "" ]]; then export NODE_VERSION=""; else export NODE_VERSION="-${1}"; fi 143 | if [[ ${2} == "default" ]]; then export TAG_SUFFIX=""; else export TAG_SUFFIX="-${2}"; fi 144 | 145 | echo "DOCKER MANIFEST: Create and Push docker manifest list - ${TARGET}:beta${NODE_VERSION}${TAG_SUFFIX}." 146 | 147 | docker manifest create ${TARGET}:beta${NODE_VERSION}${TAG_SUFFIX} \ 148 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-amd64 \ 149 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v6 \ 150 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v7 \ 151 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm64v8 \ 152 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-s390x \ 153 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-i386 154 | 155 | docker manifest annotate ${TARGET}:beta${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v6 --os=linux --arch=arm --variant=v6 156 | docker manifest annotate ${TARGET}:beta${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v7 --os=linux --arch=arm --variant=v7 157 | docker manifest annotate ${TARGET}:beta${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm64v8 --os=linux --arch=arm64 --variant=v8 158 | docker manifest annotate ${TARGET}:beta${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-s390x --os=linux --arch=s390x 159 | docker manifest annotate ${TARGET}:beta${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-i386 --os=linux --arch=386 160 | 161 | docker manifest push ${TARGET}:beta${NODE_VERSION}${TAG_SUFFIX} 162 | 163 | docker run --rm mplatform/mquery ${TARGET}:beta${NODE_VERSION}${TAG_SUFFIX} 164 | } 165 | 166 | function docker_manifest_list_dev() { 167 | if [[ ${1} == "" ]]; then export NODE_VERSION=""; else export NODE_VERSION="-${1}"; fi 168 | if [[ ${2} == "default" ]]; then export TAG_SUFFIX=""; else export TAG_SUFFIX="-${2}"; fi 169 | 170 | echo "DOCKER MANIFEST: Create and Push docker manifest list - ${TARGET}:dev${NODE_VERSION}${TAG_SUFFIX}." 171 | 172 | docker manifest create ${TARGET}:dev${NODE_VERSION}${TAG_SUFFIX} \ 173 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-amd64 \ 174 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v6 \ 175 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v7 \ 176 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm64v8 \ 177 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-s390x \ 178 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-i386 179 | 180 | docker manifest annotate ${TARGET}:dev${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v6 --os=linux --arch=arm --variant=v6 181 | docker manifest annotate ${TARGET}:dev${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v7 --os=linux --arch=arm --variant=v7 182 | docker manifest annotate ${TARGET}:dev${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm64v8 --os=linux --arch=arm64 --variant=v8 183 | docker manifest annotate ${TARGET}:dev${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-s390x --os=linux --arch=s390x 184 | docker manifest annotate ${TARGET}:dev${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-i386 --os=linux --arch=386 185 | 186 | docker manifest push ${TARGET}:dev${NODE_VERSION}${TAG_SUFFIX} 187 | 188 | docker run --rm mplatform/mquery ${TARGET}:dev${NODE_VERSION}${TAG_SUFFIX} 189 | } 190 | 191 | function docker_manifest_list_test() { 192 | if [[ ${1} == "" ]]; then export NODE_VERSION=""; else export NODE_VERSION="-${1}"; fi 193 | if [[ ${2} == "default" ]]; then export TAG_SUFFIX=""; else export TAG_SUFFIX="-${2}"; fi 194 | 195 | echo "DOCKER MANIFEST: Create and Push docker manifest list - ${TARGET}:test${NODE_VERSION}${TAG_SUFFIX}." 196 | 197 | docker manifest create ${TARGET}:test${NODE_VERSION}${TAG_SUFFIX} \ 198 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-amd64 \ 199 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v6 \ 200 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v7 \ 201 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm64v8 \ 202 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-s390x \ 203 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-i386 204 | 205 | docker manifest annotate ${TARGET}:test${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v6 --os=linux --arch=arm --variant=v6 206 | docker manifest annotate ${TARGET}:test${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v7 --os=linux --arch=arm --variant=v7 207 | docker manifest annotate ${TARGET}:test${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm64v8 --os=linux --arch=arm64 --variant=v8 208 | docker manifest annotate ${TARGET}:test${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-s390x --os=linux --arch=s390x 209 | docker manifest annotate ${TARGET}:test${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-i386 --os=linux --arch=386 210 | 211 | docker manifest push ${TARGET}:test${NODE_VERSION}${TAG_SUFFIX} 212 | 213 | docker run --rm mplatform/mquery ${TARGET}:test${NODE_VERSION}${TAG_SUFFIX} 214 | } 215 | 216 | function docker_manifest_list_latest() { 217 | if [[ ${1} == "" ]]; then export NODE_VERSION=""; else export NODE_VERSION="-${1}"; fi 218 | if [[ ${2} == "default" ]]; then export TAG_SUFFIX=""; else export TAG_SUFFIX="-${2}"; fi 219 | 220 | echo "DOCKER MANIFEST: Create and Push docker manifest list - ${TARGET}:latest${NODE_VERSION}${TAG_SUFFIX}." 221 | 222 | docker manifest create ${TARGET}:latest${NODE_VERSION}${TAG_SUFFIX} \ 223 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-amd64 \ 224 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v6 \ 225 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v7 \ 226 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm64v8 \ 227 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-s390x \ 228 | ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-i386 229 | 230 | docker manifest annotate ${TARGET}:latest${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v6 --os=linux --arch=arm --variant=v6 231 | docker manifest annotate ${TARGET}:latest${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm32v7 --os=linux --arch=arm --variant=v7 232 | docker manifest annotate ${TARGET}:latest${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-arm64v8 --os=linux --arch=arm64 --variant=v8 233 | docker manifest annotate ${TARGET}:latest${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-s390x --os=linux --arch=s390x 234 | docker manifest annotate ${TARGET}:latest${NODE_VERSION}${TAG_SUFFIX} ${TARGET}:${BUILD_VERSION}${NODE_VERSION:--10}${TAG_SUFFIX}-i386 --os=linux --arch=386 235 | 236 | docker manifest push ${TARGET}:latest${NODE_VERSION}${TAG_SUFFIX} 237 | 238 | docker run --rm mplatform/mquery ${TARGET}:latest${NODE_VERSION}${TAG_SUFFIX} 239 | } 240 | 241 | function setup_dependencies() { 242 | echo "PREPARE: Setting up dependencies." 243 | sudo apt update -y 244 | sudo apt install --only-upgrade docker-ce -y 245 | } 246 | 247 | function update_docker_configuration() { 248 | echo "PREPARE: Updating docker configuration" 249 | 250 | mkdir $HOME/.docker 251 | 252 | # enable experimental to use docker manifest command 253 | echo '{ 254 | "experimental": "enabled" 255 | }' | tee $HOME/.docker/config.json 256 | 257 | # enable experimental 258 | echo '{ 259 | "experimental": true, 260 | "storage-driver": "overlay2", 261 | "max-concurrent-downloads": 50, 262 | "max-concurrent-uploads": 50 263 | }' | sudo tee /etc/docker/daemon.json 264 | 265 | sudo service docker restart 266 | } 267 | 268 | function prepare_qemu() { 269 | echo "PREPARE: Qemu" 270 | # Prepare qemu to build non amd64 / x86_64 images 271 | docker run --rm --privileged multiarch/qemu-user-static:register --reset 272 | mkdir tmp 273 | pushd tmp && 274 | curl -L -o qemu-x86_64-static.tar.gz https://github.com/multiarch/qemu-user-static/releases/download/$QEMU_VERSION/qemu-x86_64-static.tar.gz && tar xzf qemu-x86_64-static.tar.gz && 275 | curl -L -o qemu-arm-static.tar.gz https://github.com/multiarch/qemu-user-static/releases/download/$QEMU_VERSION/qemu-arm-static.tar.gz && tar xzf qemu-arm-static.tar.gz && 276 | curl -L -o qemu-aarch64-static.tar.gz https://github.com/multiarch/qemu-user-static/releases/download/$QEMU_VERSION/qemu-aarch64-static.tar.gz && tar xzf qemu-aarch64-static.tar.gz && 277 | curl -L -o qemu-s390x-static.tar.gz https://github.com/multiarch/qemu-user-static/releases/download/$QEMU_VERSION/qemu-s390x-static.tar.gz && tar xzf qemu-s390x-static.tar.gz && 278 | curl -L -o qemu-i386-static.tar.gz https://github.com/multiarch/qemu-user-static/releases/download/$QEMU_VERSION/qemu-i386-static.tar.gz && tar xzf qemu-i386-static.tar.gz && 279 | popd 280 | } 281 | 282 | main "$1" "$2" "$3" 283 | -------------------------------------------------------------------------------- /.docker/healthcheck.js: -------------------------------------------------------------------------------- 1 | ../docker-custom/healthcheck.js -------------------------------------------------------------------------------- /.docker/known_hosts.sh: -------------------------------------------------------------------------------- 1 | ../docker-custom/known_hosts.sh -------------------------------------------------------------------------------- /.docker/scripts/entrypoint.sh: -------------------------------------------------------------------------------- 1 | ../../docker-custom/scripts/entrypoint.sh -------------------------------------------------------------------------------- /.docker/scripts/install_devtools.sh: -------------------------------------------------------------------------------- 1 | ../../docker-custom/scripts/install_devtools.sh -------------------------------------------------------------------------------- /.docker/scripts/remove_native_gpio.sh: -------------------------------------------------------------------------------- 1 | ../../docker-custom/scripts/remove_native_gpio.sh -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Declare files that will always have LF line endings on checkout. 2 | *.sh text eol=lf -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 22 | 23 | ### What are the steps to reproduce? 24 | 25 | ### What happens? 26 | 27 | ### What do you expect to happen? 28 | 29 | ### Please tell us about your environment: 30 | 31 | [ ] Platform/OS: 32 | 33 | [ ] Browser: 34 | 35 | - If you are a Portainer user, please attach a screenshot of your container details. 36 | 37 | ``` 38 | Containers -> Click on the node-red container -> Scroll to Container Details and take a screenshot 39 | ``` 40 | 41 | - If you are a command line user please execute the following command and attach the log file. 42 | 43 | ```shell 44 | docker inspect > container.log 45 | ``` 46 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 12 | 13 | - [ ] Bugfix (non-breaking change which fixes an issue) 14 | - [ ] New feature (non-breaking change which adds functionality) 15 | 16 | 23 | 24 | ## Proposed changes 25 | 26 | 27 | 28 | ## Checklist 29 | 30 | 31 | - [ ] I have read the [contribution guidelines](https://github.com/node-red/node-red/blob/master/CONTRIBUTING.md) 32 | - [ ] For non-bugfix PRs, I have discussed this change on the mailing list/slack team. 33 | - [ ] I have run `grunt` to verify the unit tests pass 34 | - [ ] I have added suitable unit tests to cover the new/changed functionality 35 | -------------------------------------------------------------------------------- /.github/no-response.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-no-response - https://github.com/probot/no-response 2 | 3 | # Number of days of inactivity before an Issue is closed for lack of response 4 | daysUntilClose: 14 5 | # Label requiring a response 6 | responseRequiredLabel: needs more info 7 | # Comment to post when closing an Issue for lack of response. Set to `false` to disable 8 | closeComment: > 9 | This issue has been automatically closed because there has been no response 10 | to our request for more information from the original author. With only the 11 | information that is currently in the issue, we don't have enough information 12 | to take action. Please reach out if you have or find the answers we need so 13 | that we can investigate further. 14 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Docker Build 4 | 5 | # Controls when the action will run. 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | version: 10 | required: true 11 | type: string 12 | description: "Version" 13 | release: 14 | types: [published] 15 | 16 | 17 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 18 | jobs: 19 | # This workflow contains a 2 jobs called "alpine" and "debian" 20 | alpine: 21 | env: 22 | # Setting the defaults up front 23 | LATEST_NODE: 20 24 | DEFAULT_IMAGE: nodered/node-red 25 | GH_IMAGE: ghcr.io/node-red/node-red 26 | DEV_IMAGE: nodered/node-red-dev 27 | GH_DEV_IMAGE: ghcr.io/node-red/node-red-dev 28 | runs-on: ubuntu-latest 29 | 30 | strategy: 31 | max-parallel: 2 32 | matrix: 33 | node: [18, 20, 22] 34 | suffix: ["", "-minimal"] 35 | 36 | # Steps represent a sequence of tasks that will be executed as part of the job 37 | steps: 38 | - 39 | name: Checkout 40 | uses: actions/checkout@v4.1.2 41 | - name: Show Env 42 | run: env 43 | - 44 | name: Docker Metadata 45 | id: meta 46 | uses: docker/metadata-action@v5 47 | with: 48 | flavor: | 49 | latest=false 50 | suffix=-${{matrix.node}}${{matrix.suffix}} 51 | images: | 52 | ${{ env.DEFAULT_IMAGE }} 53 | ${{ env.GH_IMAGE }} 54 | tags: | 55 | type=ref,event=branch 56 | type=semver,pattern={{version}} 57 | 58 | - 59 | name: Setup QEMU 60 | uses: docker/setup-qemu-action@v3 61 | with: 62 | platforms: arm64 63 | - 64 | name: Set up SSH key pi5 65 | uses: MrSquaare/ssh-setup-action@2d028b70b5e397cf8314c6eaea229a6c3e34977a # v3.1.0 66 | with: 67 | host: ${{ secrets.SSH_REMOTE_HOST}} 68 | private-key: ${{ secrets.SSH_PRIVATE_KEY }} 69 | private-key-name: remote 70 | port: 2022 71 | - 72 | name: Set up SSH key pi4 73 | uses: MrSquaare/ssh-setup-action@2d028b70b5e397cf8314c6eaea229a6c3e34977a # v3.1.0 74 | with: 75 | host: ${{ secrets.SSH_REMOTE_HOST}} 76 | private-key: ${{ secrets.SSH_PRIVATE_KEY }} 77 | private-key-name: remote 78 | port: 2023 79 | - 80 | name: Setup Docker buildx 81 | uses: docker/setup-buildx-action@v3 82 | with: 83 | append: | 84 | - endpoint: ssh://nr@${{ secrets.SSH_REMOTE_HOST }}:2023 85 | platforms: linux/arm/v6,linux/arm/v7 86 | - endpoint: ssh://nr@${{ secrets.SSH_REMOTE_HOST }}:2022 87 | platforms: linux/arm64 88 | - name: Get Date 89 | id: date 90 | # run: echo "::set-output name=date::$(date +'%Y-%m-%dT%H:%M:%SZ')" 91 | run: echo "date=$(date +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT 92 | - name: Get Node-RED Version 93 | id: nrVersion 94 | run: | 95 | TAGS="" 96 | while IFS= read -r TAG;do 97 | if [ -z "$TAGS" ]; then 98 | TAGS=$TAG 99 | else 100 | TAGS="$TAGS,$TAG" 101 | fi 102 | done <<< "${{ steps.meta.outputs.tags }}" 103 | 104 | echo "Start Tags = $TAGS" 105 | echo "GITHUB_REF = $GITHUB_REF" 106 | 107 | echo "version = ${{ github.event.inputs.version }}" 108 | 109 | if [[ ! -z "${{ github.event.inputs.version }}" ]]; then 110 | TEMP=${{ github.event.inputs.version }} 111 | TEMP=${TEMP:1} 112 | TEMP2=$(echo $GITHUB_REF | awk -F '/' '{ print $3}') 113 | echo "$GITHUB_REF - $TEMP" 114 | TAGS=$(echo $TAGS | sed "s/$TEMP2/$TEMP/g") 115 | TRAVIS_TAG=${{ github.event.inputs.version }} 116 | else 117 | TRAVIS_TAG=$(echo $GITHUB_REF | awk -F '/' '{ print $3}') 118 | fi 119 | 120 | echo "TRAVIS_TAG = $TRAVIS_TAG" 121 | 122 | if [[ "$TRAVIS_TAG" =~ ^v[0-9\.-]*$ ]]; then 123 | IMAGE=${{ env.DEFAULT_IMAGE }} 124 | GH_IMAGE=${{ env.GH_IMAGE}} 125 | PUSH=true 126 | VERSION=${TRAVIS_TAG:1} 127 | STABLE_VERSION=`echo ${VERSION} | sed -r 's/^([[:digit:]]+)\.([[:digit:]]+)\.([[:digit:]]+)$/\1.\2/'` 128 | 129 | if [[ "${{ matrix.node }}" == "${{ env.LATEST_NODE }}" && "${{ matrix.suffix }}" == "" ]]; then 130 | TAGS="$TAGS,$IMAGE:$VERSION,$IMAGE:$STABLE_VERSION,$IMAGE:latest,$GH_IMAGE:$VERSION,$GH_IMAGE:$STABLE_VERSION,$GH_IMAGE:latest" 131 | elif [[ "${{ matrix.node }}" == "${{ env.LATEST_NODE }}" && "${{ matrix.suffix }}" == "-minimal" ]]; then 132 | TAGS="$TAGS,$IMAGE:$VERSION-minimal,$IMAGE:latest-minimal,$GH_IMAGE:$VERSION-minimal,$GH_IMAGE:latest-minimal" 133 | fi 134 | 135 | TAGS="$TAGS,$IMAGE:latest-${{ matrix.node }}${{ matrix.suffix }},$GH_IMAGE:latest-${{ matrix.node }}${{ matrix.suffix }}" 136 | 137 | else 138 | IMAGE=${{ env.DEV_IMAGE }} 139 | GH_IMAGE=${{ env.GH_DEV_IMAGE}} 140 | if [[ "$TRAVIS_TAG" == *"dev"* || "$TRAVIS_TAG" == *"beta"* ]]; then 141 | PUSH=true 142 | else 143 | PUSH=false 144 | fi 145 | VERSION=${TRAVIS_TAG} 146 | TAGS=$(echo $TAGS | sed 's!${{ env.DEFAULT_IMAGE}}!${{ env.DEV_IMAGE }}!') 147 | TAGS=$(echo $TAGS | sed 's!${{ env.GH_IMAGE}}!${{ env.GH_DEV_IMAGE }}!') 148 | if [ "${{ matrix.node }}" == "${{ env.LATEST_NODE }}" ] && [ "${{ matrix.suffix}}" == "" ]; then 149 | TAGS="$TAGS,$IMAGE:$VERSION,$GH_IMAGE:$VERSION" 150 | fi 151 | fi 152 | 153 | # if [[ "${{ matrix.node }}" == "18"]]; then 154 | # echo "platforms=linux/amd64,linux/arm/v7,linux/arm64" >> $GITHUB_OUTPUT 155 | # else 156 | # echo "platforms=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT 157 | # fi 158 | 159 | # if [[ "${{ matrix.suffix }}" == "-minimal" ]]; then 160 | # echo "platforms=linux/amd64,linux/arm64" >> $GITHUB_OUTPUT 161 | # else 162 | # echo "platforms=linux/amd64,linux/arm/v7,linux/arm64" >> $GITHUB_OUTPUT 163 | # fi 164 | echo "platforms=linux/amd64,linux/arm/v7,linux/arm64" >> $GITHUB_OUTPUT 165 | 166 | echo $TAGS 167 | echo "tags=$TAGS" >> $GITHUB_OUTPUT 168 | echo "push=$PUSH" >> $GITHUB_OUTPUT 169 | echo "version=$TRAVIS_TAG" >> $GITHUB_OUTPUT 170 | echo "buildVersion=$VERSION" >> $GITHUB_OUTPUT 171 | 172 | cat $GITHUB_OUTPUT 173 | 174 | # echo "::set-output name=tags::$TAGS" 175 | # echo "::set-output name=push::$PUSH" 176 | # echo "::set-output name=version::$TRAVIS_TAG" 177 | # echo "::set-output name=buildVersion::$VERSION" 178 | - 179 | name: Login to DockerHub 180 | uses: docker/login-action@v3 181 | with: 182 | username: ${{ secrets.DOCKERHUB_USERNAME }} 183 | password: ${{ secrets.DOCKERHUB_TOKEN }} 184 | - 185 | name: Login to GitHub Container Registry 186 | uses: docker/login-action@v3 187 | with: 188 | registry: ghcr.io 189 | username: ${{ github.repository_owner }} 190 | password: ${{ secrets.GITHUB_TOKEN }} 191 | - 192 | name: Build and push 193 | id: build-push 194 | uses: docker/build-push-action@v5 195 | with: 196 | context: . 197 | platforms: ${{ steps.nrVersion.outputs.platforms }} 198 | push: ${{ steps.nrVersion.outputs.push }} 199 | file: .docker/Dockerfile.alpine 200 | build-args: | 201 | NODE_VERSION=${{ matrix.node }} 202 | BUILD_DATE=${{ steps.date.outputs.date }} 203 | BUILD_VERSION=${{ steps.nrVersion.outputs.buildVersion }} 204 | BUILD_REF=${{ env.GITHUB_SHA }} 205 | NODE_RED_VERSION=${{ steps.nrVersion.outputs.version }} 206 | TAG_SUFFIX=${{ matrix.suffix }} 207 | 208 | tags: ${{ steps.nrVersion.outputs.tags }} 209 | 210 | debian: 211 | env: 212 | # Setting the defaults up front 213 | LATEST_NODE: 20 214 | DEFAULT_IMAGE: nodered/node-red 215 | GH_IMAGE: ghcr.io/node-red/node-red 216 | DEV_IMAGE: nodered/node-red-dev 217 | GH_DEV_IMAGE: ghcr.io/node-red/node-red-dev 218 | runs-on: ubuntu-latest 219 | 220 | steps: 221 | - name: Checkout 222 | uses: actions/checkout@v4.1.2 223 | - name: Setup QEMU 224 | uses: docker/setup-qemu-action@v3 225 | with: 226 | image: tonistiigi/binfmt:qemu-v7.0.0-28 227 | - name: Setup Docker buildx 228 | uses: docker/setup-buildx-action@v3 229 | - name: Login to DockerHub 230 | uses: docker/login-action@v3 231 | with: 232 | username: ${{ secrets.DOCKERHUB_USERNAME }} 233 | password: ${{ secrets.DOCKERHUB_TOKEN }} 234 | - name: Login to GitHub Container Registry 235 | uses: docker/login-action@v3 236 | with: 237 | registry: ghcr.io 238 | username: ${{ github.repository_owner }} 239 | password: ${{ secrets.GITHUB_TOKEN }} 240 | - name: Get Date 241 | id: date 242 | # run: echo "::set-output name=date::$(date +'%Y-%m-%dT%H:%M:%SZ')" 243 | run : echo "date=$(date +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT 244 | - name: Docker Metadata 245 | id: meta 246 | uses: docker/metadata-action@v5 247 | with: 248 | flavor: | 249 | latest=false 250 | images: | 251 | ${{ env.DEFAULT_IMAGE }} 252 | ${{ env.GH_IMAGE }} 253 | tags: | 254 | type=ref,event=branch 255 | type=semver,pattern={{version}} 256 | 257 | - name: Get Node-RED Version 258 | id: nrVersion 259 | run: | 260 | TAGS="" 261 | while IFS= read -r TAG;do 262 | if [ -z "$TAGS" ]; then 263 | TAGS=$TAG 264 | else 265 | TAGS="$TAGS,$TAG" 266 | fi 267 | done <<< "${{ steps.meta.outputs.tags }}" 268 | 269 | echo "Original tags $TAGS" 270 | 271 | if [[ ! -z "${{ github.event.inputs.version }}" ]]; then 272 | TRAVIS_TAG=${{ github.event.inputs.version }} 273 | else 274 | TRAVIS_TAG=$(echo $GITHUB_REF | awk -F '/' '{ print $3}') 275 | fi 276 | 277 | if [[ "$TRAVIS_TAG" =~ ^v[0-9\.-]*$ ]]; then 278 | # release build 279 | IMAGE=${{ env.DEFAULT_IMAGE }} 280 | GH_IMAGE=${{ env.GH_IMAGE }} 281 | PUSH=true 282 | VERSION=${TRAVIS_TAG:1} 283 | STABLE_VERSION=`echo ${VERSION} | sed -r 's/^([[:digit:]]+)\.([[:digit:]]+)\.([[:digit:]]+)$/\1.\2/'` 284 | 285 | TAGS="$IMAGE:latest-debian,$IMAGE:$VERSION-debian,$IMAGE:$STABLE_VERSION-debian,$GH_IMAGE:latest-debian,$GH_IMAGE:$VERSION-debian,$GH_IMAGE:$STABLE_VERSION-debian" 286 | 287 | else 288 | IMAGE=${{ env.DEV_IMAGE }} 289 | GH_IMAGE=${{ env.GH_DEV_IMAGE}} 290 | if [[ "$TRAVIS_TAG" == *"dev"* || "$TRAVIS_TAG" == *"beta"* ]]; then 291 | # beta build 292 | PUSH=true 293 | else 294 | PUSH=false 295 | fi 296 | VERSION=${TRAVIS_TAG} 297 | 298 | TAGS="$IMAGE:$VERSION-debian,$GH_DEV_IMAGE:$VERSION" 299 | 300 | fi 301 | 302 | echo $TAGS 303 | echo "tags=$TAGS" >> $GITHUB_OUTPUT 304 | echo "push=$PUSH" >> $GITHUB_OUTPUT 305 | echo "version=$TRAVIS_TAG" >> $GITHUB_OUTPUT 306 | echo "buildVersion=$VERSION" >> $GITHUB_OUTPUT 307 | 308 | cat $GITHUB_OUTPUT 309 | 310 | - name: Build and push 311 | id: build-push 312 | uses: docker/build-push-action@v5 313 | with: 314 | context: . 315 | platforms: linux/amd64, linux/arm64, linux/arm/v7 316 | file: .docker/Dockerfile.debian 317 | push: ${{ steps.nrVersion.outputs.push }} 318 | build-args: | 319 | NODE_VERSION=${{ env.LATEST_NODE }} 320 | BUILD_DATE=${{ steps.date.outputs.date }} 321 | BUILD_REF=${{ env.GITHUB.SHA }} 322 | TAG_SUFFIX=-debian 323 | NODE_RED_VERSION=${{ steps.nrVersion.outputs.version }} 324 | BUILD_VERSION=${{ steps.nrVersion.outputs.buildVersion }} 325 | tags: ${{ steps.nrVersion.outputs.tags }} 326 | 327 | 328 | -------------------------------------------------------------------------------- /.github/workflows/pr.yaml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Manual Build Test for PRs 4 | 5 | # Controls when the action will run. 6 | on: 7 | workflow_dispatch: 8 | 9 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 10 | jobs: 11 | # This workflow contains a single job called "build" 12 | build: 13 | env: 14 | # Setting the defaults up front 15 | LATEST_NODE: 14 16 | DEFAULT_IMAGE: nodered/node-red 17 | DEV_IMAGE: nodered/node-red-dev 18 | runs-on: ubuntu-latest 19 | 20 | strategy: 21 | matrix: 22 | node: [12, 14] 23 | suffix: ["", "-minimal"] 24 | 25 | # Steps represent a sequence of tasks that will be executed as part of the job 26 | steps: 27 | - 28 | name: Checkout 29 | uses: actions/checkout@v2 30 | - name: Show Env 31 | run: env 32 | - 33 | name: Docker Metadata 34 | id: meta 35 | uses: docker/metadata-action@v3 36 | with: 37 | flavor: | 38 | latest=false 39 | suffix=-${{matrix.node}}${{matrix.suffix}} 40 | images: | 41 | ${{ env.DEFAULT_IMAGE }} 42 | tags: | 43 | type=ref,event=branch 44 | type=semver,pattern={{version}} 45 | 46 | - 47 | name: Setup QEMU 48 | uses: docker/setup-qemu-action@v1 49 | - 50 | name: Setup Docker buildx 51 | uses: docker/setup-buildx-action@v1 52 | - name: Get Date 53 | id: date 54 | run: echo "::set-output name=date::$(date +'%Y-%m-%dT%H:%M:%SZ')" 55 | - name: Get Node-RED Version 56 | id: nrVersion 57 | run: | 58 | TAGS="nodered/node-red-dev:pr-test" 59 | while IFS= read -r TAG;do 60 | if [ -z "$TAGS" ]; then 61 | TAGS=$TAG 62 | else 63 | TAGS="$TAGS,$TAG" 64 | fi 65 | done <<< "${{ steps.meta.outputs.tags }}" 66 | 67 | TRAVIS_TAG=$(echo $GITHUB_REF | awk -F '/' '{ print $3}') 68 | if [[ "$TRAVIS_TAG" =~ ^v[0-9\.-]*$ ]]; then 69 | IMAGE=${{ env.DEFAULT_IMAGE }} 70 | VERSION=${TRAVIS_TAG:1} 71 | if [ "${{ matrix.node }}" == "${{ env.LATEST_NODE }}" ] && [ "${{ matrix.suffix}}" == "" ]; then 72 | TAGS="$TAGS,$IMAGE:$VERSION,$IMAGE:latest" 73 | fi 74 | else 75 | IMAGE=${{ env.DEV_IMAGE }} 76 | VERSION=${TRAVIS_TAG} 77 | TAGS=$(echo $TAGS | sed 's!${{ env.DEFAULT_IMAGE}}!${{ env.DEV_IMAGE }}!') 78 | if [ "${{ matrix.node }}" == "${{ env.LATEST_NODE }}" ] && [ "${{ matrix.suffix}}" == "" ]; then 79 | TAGS="$TAGS,$IMAGE:$VERSION" 80 | fi 81 | fi 82 | 83 | echo $TAGS 84 | 85 | echo "::set-output name=tags::$TAGS" 86 | echo "::set-output name=version::$(echo $GITHUB_REF | awk -F '/' '{ print $3}')" 87 | echo "::set-output name=buildVersion::$VERSION" 88 | # - 89 | # name: Login to DockerHub 90 | # uses: docker/login-action@v1 91 | # with: 92 | # username: ${{ secrets.DOCKERHUB_USERNAME }} 93 | # password: ${{ secrets.DOCKERHUB_TOKEN }} 94 | - 95 | name: Build and push 96 | id: build-push 97 | uses: docker/build-push-action@v2 98 | continue-on-error: true 99 | with: 100 | context: . 101 | platforms: linux/amd64, linux/arm64, linux/s390x, linux/arm/v7 102 | push: false 103 | file: .docker/Dockerfile.alpine 104 | build-args: | 105 | NODE_VERSION=${{ matrix.node }} 106 | BUILD_DATE=${{ steps.date.outputs.date }} 107 | BUILD_VERSION=${{ steps.nrVersion.outputs.buildVersion }} 108 | BUILD_REF=${{ env.GITHUB_SHA }} 109 | NODE_RED_VERSION=v${{ steps.nrVersion.outputs.version }} 110 | TAG_SUFFIX=${{ matrix.suffix }} 111 | 112 | tags: ${{ steps.nrVersion.outputs.tags }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | /node-red-docker.iml 3 | /.idea/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | dist: xenial 4 | 5 | services: docker 6 | 7 | language: bash 8 | 9 | env: 10 | global: 11 | - QEMU_VERSION=v4.0.0 12 | - OS=alpine 13 | - DOCKER_FILE=Dockerfile.alpine 14 | 15 | matrix: 16 | ### Node JS 10 ##################################################################################################### 17 | # Default Images 18 | - NODE_VERSION=10 TAG_SUFFIX=default QEMU_ARCH=x86_64 ARCH=amd64 19 | - NODE_VERSION=10 TAG_SUFFIX=default QEMU_ARCH=arm ARCH=arm32v6 20 | - NODE_VERSION=10 TAG_SUFFIX=default QEMU_ARCH=arm ARCH=arm32v7 21 | - NODE_VERSION=10 TAG_SUFFIX=default QEMU_ARCH=aarch64 ARCH=arm64v8 22 | - NODE_VERSION=10 TAG_SUFFIX=default QEMU_ARCH=s390x ARCH=s390x 23 | - NODE_VERSION=10 TAG_SUFFIX=default QEMU_ARCH=i386 ARCH=i386 24 | 25 | # Minimal Images 26 | - NODE_VERSION=10 TAG_SUFFIX=minimal QEMU_ARCH=x86_64 ARCH=amd64 27 | - NODE_VERSION=10 TAG_SUFFIX=minimal QEMU_ARCH=arm ARCH=arm32v6 28 | - NODE_VERSION=10 TAG_SUFFIX=minimal QEMU_ARCH=arm ARCH=arm32v7 29 | - NODE_VERSION=10 TAG_SUFFIX=minimal QEMU_ARCH=aarch64 ARCH=arm64v8 30 | - NODE_VERSION=10 TAG_SUFFIX=minimal QEMU_ARCH=s390x ARCH=s390x 31 | - NODE_VERSION=10 TAG_SUFFIX=minimal QEMU_ARCH=i386 ARCH=i386 32 | 33 | ### Node JS 12 ##################################################################################################### 34 | # Default Images 35 | - NODE_VERSION=12 TAG_SUFFIX=default QEMU_ARCH=x86_64 ARCH=amd64 36 | - NODE_VERSION=12 TAG_SUFFIX=default QEMU_ARCH=arm ARCH=arm32v6 37 | - NODE_VERSION=12 TAG_SUFFIX=default QEMU_ARCH=arm ARCH=arm32v7 38 | - NODE_VERSION=12 TAG_SUFFIX=default QEMU_ARCH=aarch64 ARCH=arm64v8 39 | - NODE_VERSION=12 TAG_SUFFIX=default QEMU_ARCH=s390x ARCH=s390x 40 | - NODE_VERSION=12 TAG_SUFFIX=default QEMU_ARCH=i386 ARCH=i386 41 | 42 | # Minimal Images 43 | - NODE_VERSION=12 TAG_SUFFIX=minimal QEMU_ARCH=x86_64 ARCH=amd64 44 | - NODE_VERSION=12 TAG_SUFFIX=minimal QEMU_ARCH=arm ARCH=arm32v6 45 | - NODE_VERSION=12 TAG_SUFFIX=minimal QEMU_ARCH=arm ARCH=arm32v7 46 | - NODE_VERSION=12 TAG_SUFFIX=minimal QEMU_ARCH=aarch64 ARCH=arm64v8 47 | - NODE_VERSION=12 TAG_SUFFIX=minimal QEMU_ARCH=s390x ARCH=s390x 48 | - NODE_VERSION=12 TAG_SUFFIX=minimal QEMU_ARCH=i386 ARCH=i386 49 | 50 | before_install: 51 | - ./.docker/docker.sh prepare 52 | 53 | install: true 54 | 55 | before_script: 56 | # Set TARGET Docker Repo 57 | # default TARGET = nodered/node-red-dev 58 | # if TRAVIS_TAG starts with a `v` and only contains numbers, dots and/or dash then TARGET = nodered/node-red 59 | - > 60 | export TARGET=nodered/node-red-dev 61 | 62 | if [[ "${TRAVIS_TAG}" =~ ^v[0-9\.-]*$ ]]; then 63 | export TARGET=nodered/node-red 64 | fi 65 | 66 | # Set NODE_RED_VERSION from package.json 67 | - > 68 | export NODE_RED_VERSION=$(grep -oE "\"node-red\": \"(\w*.\w*.\w*.\w*.\w*.)" package.json | cut -d\" -f4) 69 | 70 | # Set BUILD_VERSION 71 | - > 72 | if [ ! -z "${TRAVIS_TAG}" ]; then 73 | export BUILD_VERSION=${TRAVIS_TAG:1}; 74 | fi 75 | 76 | script: 77 | # Build Docker image 78 | - ./.docker/docker.sh build 79 | 80 | # Test Docker image 81 | - ./.docker/docker.sh test 82 | 83 | # Push Docker image, only if TRAVIS_TAG is set 84 | - > 85 | if [ ! -z "${TRAVIS_TAG}" ]; then 86 | # Tag Docker image 87 | ./.docker/docker.sh tag 88 | 89 | # Docker Login 90 | echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin 91 | 92 | # Push Docker image 93 | ./.docker/docker.sh push 94 | 95 | # Docker Logout 96 | docker logout 97 | fi 98 | 99 | jobs: 100 | include: 101 | - stage: manifest 102 | # Only create and push manifest list to Docker Hub, when tag starts with a `v`, eg. v1.0.2 103 | if: tag =~ ^v 104 | script: 105 | # Create and push Docker manifest lists 106 | # The push order is displayed in reverse order on Docker Hub 107 | 108 | # Docker Login 109 | - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin 110 | 111 | # Create and push manifest list `version` for minimal 112 | - ./.docker/docker.sh manifest-list-version "12" "minimal" 113 | - ./.docker/docker.sh manifest-list-version "10" "minimal" 114 | - ./.docker/docker.sh manifest-list-version "" "minimal" 115 | 116 | # Create and push manifest list `version` for default 117 | - ./.docker/docker.sh manifest-list-version "12" "default" 118 | - ./.docker/docker.sh manifest-list-version "10" "default" 119 | - ./.docker/docker.sh manifest-list-version "" "default" 120 | 121 | # if TARGET = `nodered/node-red` then manifest lists get tagged as `latest` and push to `nodered/node-red` 122 | # if tags contain `beta` then manifest lists get tagged as `beta` and push to `nodered/node-red-dev` 123 | # if tags contain `dev` then manifest lists get tagged as `dev` and push to `nodered/node-red-dev` 124 | # else manifest lists get tagged as `test` and push to `nodered/node-red-dev` 125 | - > 126 | if [[ "${TARGET}" == "nodered/node-red" ]]; then 127 | # Create and push manifest list `latest` for minimal 128 | ./.docker/docker.sh manifest_list_latest "12" "minimal" 129 | ./.docker/docker.sh manifest_list_latest "10" "minimal" 130 | ./.docker/docker.sh manifest_list_latest "" "minimal" 131 | 132 | # Create and push manifest list `latest` for default 133 | ./.docker/docker.sh manifest_list_latest "12" "default" 134 | ./.docker/docker.sh manifest_list_latest "10" "default" 135 | ./.docker/docker.sh manifest_list_latest "" "default" 136 | elif [[ "${TRAVIS_TAG}" == *"beta"* ]]; then 137 | # Create and push manifest list `beta` for minimal 138 | ./.docker/docker.sh manifest_list_beta "12" "minimal" 139 | ./.docker/docker.sh manifest_list_beta "10" "minimal" 140 | ./.docker/docker.sh manifest_list_beta "" "minimal" 141 | 142 | # Create and push manifest list `beta` for default 143 | ./.docker/docker.sh manifest_list_beta "12" "default" 144 | ./.docker/docker.sh manifest_list_beta "10" "default" 145 | ./.docker/docker.sh manifest_list_beta "" "default" 146 | elif [[ "${TRAVIS_TAG}" == *"dev"* ]]; then 147 | # Create and push manifest list `dev` for minimal 148 | ./.docker/docker.sh manifest_list_dev "12" "minimal" 149 | ./.docker/docker.sh manifest_list_dev "10" "minimal" 150 | ./.docker/docker.sh manifest_list_dev "" "minimal" 151 | 152 | # Create and push manifest list `dev` for default 153 | ./.docker/docker.sh manifest_list_dev "12" "default" 154 | ./.docker/docker.sh manifest_list_dev "10" "default" 155 | ./.docker/docker.sh manifest_list_dev "" "default" 156 | else 157 | # Create and push manifest list `test` for minimal 158 | ./.docker/docker.sh manifest_list_test "12" "minimal" 159 | ./.docker/docker.sh manifest_list_test "10" "minimal" 160 | ./.docker/docker.sh manifest_list_test "" "minimal" 161 | 162 | # Create and push manifest list `test` for default 163 | ./.docker/docker.sh manifest_list_test "12" "default" 164 | ./.docker/docker.sh manifest_list_test "10" "default" 165 | ./.docker/docker.sh manifest_list_test "" "default" 166 | fi 167 | 168 | # Docker Logout 169 | - docker logout 170 | 171 | # Notify me when things fail 172 | notifications: 173 | email: true 174 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Node-RED 2 | 3 | We welcome contributions, but request you follow these guidelines. 4 | 5 | - [Raising issues](#raising-issues) 6 | - [Feature requests](#feature-requests) 7 | - [Pull-Requests](#pull-requests) 8 | 9 | This project adheres to the [Contributor Covenant 1.4](http://contributor-covenant.org/version/1/4/). 10 | By participating, you are expected to uphold this code. Please report unacceptable 11 | behavior to the project's core team at team@nodered.org. 12 | 13 | ## Raising issues 14 | 15 | Please raise any bug reports on the relevant project's issue tracker. Be sure to 16 | search the list to see if your issue has already been raised. 17 | 18 | A good bug report is one that make it easy for us to understand what you were 19 | trying to do and what went wrong. 20 | 21 | Provide as much context as possible so we can try to recreate the issue. 22 | If possible, include the relevant part of your flow. To do this, select the 23 | relevant nodes, press Ctrl-E and copy the flow data from the Export dialog. 24 | 25 | At a minimum, please include: 26 | 27 | - Version of Node-RED - either release number if you downloaded a zip, or the first few lines of `git log` if you are cloning the repository directly. 28 | - Version of node.js - what does `node -v` say? 29 | 30 | ## Feature requests 31 | 32 | For feature requests, please raise them on the [mailing list](https://discourse.nodered.org). 33 | 34 | ## Pull-Requests 35 | 36 | If you want to raise a pull-request with a new feature, or a refactoring 37 | of existing code, it may well get rejected if you haven't discussed it on 38 | the [forum](https://discourse.nodered.org) first. 39 | 40 | All contributors need to sign the JS Foundation's Contributor License Agreement. 41 | It is an online process and quick to do. You can read the details of the agreement 42 | here: https://cla.js.foundation/node-red/node-red. 43 | 44 | If you raise a pull-request without having signed the CLA, you will be prompted 45 | to do so automatically. 46 | 47 | 48 | ### Coding standards 49 | 50 | Please ensure you follow the coding standards used through-out the existing 51 | code base. Some basic rules include: 52 | 53 | - all files must have the Apache license in the header. 54 | - indent with 4-spaces, no tabs. No arguments. 55 | - opening brace on same line as `if`/`for`/`function` and so on, closing brace 56 | on its own line. 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Node-RED Docker 2 | 3 | [![Greenkeeper badge](https://badges.greenkeeper.io/node-red/node-red-docker.svg)](https://greenkeeper.io/) 4 | [![Build Status](https://travis-ci.org/node-red/node-red-docker.svg?branch=master)](https://travis-ci.org/node-red/node-red-docker) 5 | [![DockerHub Pull](https://img.shields.io/docker/pulls/nodered/node-red.svg)](https://hub.docker.com/r/nodered/node-red/) 6 | [![DockerHub Stars](https://img.shields.io/docker/stars/nodered/node-red.svg?maxAge=2592000)](https://hub.docker.com/r/nodered/node-red/) 7 | 8 | This project describes some of the many ways Node-RED can be run under Docker and has support for multiple architectures (amd64, arm32v6, arm32v7, arm64v8, i386 and s390x). 9 | Some basic familiarity with Docker and the [Docker Command Line](https://docs.docker.com/engine/reference/commandline/cli/) is assumed. 10 | 11 | **Note**: In version 1.2 we removed the named VOLUME from the build. It should not affect many users - but the details are [here](volumechanges.md). 12 | 13 | As of Node-RED 1.0 this project provides the build for the `nodered/node-red` container on [Docker Hub](https://hub.docker.com/r/nodered/node-red/). 14 | 15 | Previous 0.20.x versions are still available at https://hub.docker.com/r/nodered/node-red-docker. 16 | 17 | ## Quick Start 18 | To run in Docker in its simplest form just run: 19 | 20 | docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered nodered/node-red 21 | 22 | Let's dissect that command: 23 | 24 | docker run - run this container, initially building locally if necessary 25 | -it - attach a terminal session so we can see what is going on 26 | -p 1880:1880 - connect local port 1880 to the exposed internal port 1880 27 | -v node_red_data:/data - mount the host node_red_data directory to the container /data directory so any changes made to flows are persisted 28 | --name mynodered - give this machine a friendly local name 29 | nodered/node-red - the image to base it on - currently Node-RED v4.0.9 30 | 31 | 32 | 33 | Running that command should give a terminal window with a running instance of Node-RED. 34 | 35 | Welcome to Node-RED 36 | =================== 37 | 38 | 10 Oct 12:57:10 - [info] Node-RED version: v4.0.9 39 | 10 Oct 12:57:10 - [info] Node.js version: v18.19.0 40 | 10 Oct 12:57:10 - [info] Linux 6.6.13-100.fc38.x86_64 x64 LE 41 | 10 Oct 12:57:11 - [info] Loading palette nodes 42 | 10 Oct 12:57:16 - [info] Settings file : /data/settings.js 43 | 10 Oct 12:57:16 - [info] Context store : 'default' [module=memory] 44 | 10 Oct 12:57:16 - [info] User directory : /data 45 | 10 Oct 12:57:16 - [warn] Projects disabled : editorTheme.projects.enabled=false 46 | 10 Oct 12:57:16 - [info] Flows file : /data/flows.json 47 | 10 Oct 12:57:16 - [info] Creating new flow file 48 | 10 Oct 12:57:17 - [warn] 49 | 50 | --------------------------------------------------------------------- 51 | Your flow credentials file is encrypted using a system-generated key. 52 | 53 | If the system-generated key is lost for any reason, your credentials 54 | file will not be recoverable, you will have to delete it and re-enter 55 | your credentials. 56 | 57 | You should set your own key using the 'credentialSecret' option in 58 | your settings file. Node-RED will then re-encrypt your credentials 59 | file using your chosen key the next time you deploy a change. 60 | --------------------------------------------------------------------- 61 | 62 | 10 Oct 12:57:17 - [info] Starting flows 63 | 10 Oct 12:57:17 - [info] Started flows 64 | 10 Oct 12:57:17 - [info] Server now running at http://localhost:1880/ 65 | 66 | [...] 67 | 68 | You can then browse to `http://{host-ip}:1880` to get the familiar Node-RED desktop. 69 | 70 | 71 | The advantage of doing this is that by giving it a name (mynodered) we can manipulate it 72 | more easily, and by fixing the host port we know we are on familiar ground. 73 | Of course this does mean we can only run one instance at a time... but one step at a time folks... 74 | 75 | If we are happy with what we see, we can detach the terminal with `Ctrl-p` `Ctrl-q` - the 76 | container will keep running in the background. 77 | 78 | To reattach to the terminal (to see logging) run: 79 | 80 | $ docker attach mynodered 81 | 82 | If you need to restart the container (e.g. after a reboot or restart of the Docker daemon): 83 | 84 | $ docker start mynodered 85 | 86 | and stop it again when required: 87 | 88 | $ docker stop mynodered 89 | 90 | **Healthcheck**: to turn off the Healthcheck add `--no-healthcheck` to the run command. 91 | 92 | ## Image Variations 93 | The Node-RED images come in different variations and are supported by manifest lists (auto-detect architecture). 94 | This makes it more easy to deploy in a multi architecture Docker environment. E.g. a Docker Swarm with mix of Raspberry Pi's and amd64 nodes. 95 | 96 | The tag naming convention is `---`, where: 97 | - `` is the Node-RED version. 98 | - `` is the Node JS version. 99 | - `` is type of image and is optional, can be either _none_ or minimal. 100 | - _none_ : is the default and has Python 2 & Python 3 + devtools installed 101 | - minimal : has no Python installed and no devtools installed 102 | - `` is the architecture of the Docker host system, can be either amd64, arm32v6, arm32v7, arm64, s390x or i386. 103 | 104 | The minimal versions (without python and build tools) are not able to install nodes that require any locally compiled native code. 105 | 106 | For example - to run the latest minimal version, you would run 107 | ``` 108 | docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered nodered/node-red:latest-minimal 109 | ``` 110 | 111 | The Node-RED images are based on [official Node JS Alpine Linux](https://hub.docker.com/_/node/) images to keep them as small as possible. 112 | Using Alpine Linux reduces the built image size, but removes standard dependencies that are required for native module compilation. If you want to add dependencies with native dependencies, extend the Node-RED image with the missing packages on running containers or build new images see [docker-custom](docker-custom/README.md) and the documentation on the Node-RED site [here](https://nodered.org/docs/getting-started/docker-custom). 113 | 114 | The following table shows the variety of provided Node-RED images. 115 | 116 | | **Tag** |**Node**| **Arch** | **Python** |**Dev**| **Base Image** | 117 | |----------------------------|--------|----------|------------|-------|----------------------------| 118 | | 4.0.9-18 | 18 | amd64 | 3.x | yes | amd64/node:18-alpine | 119 | | | 18 | arm32v7 | 3.x | yes | arm32v7/node:18-alpine | 120 | | | 18 | arm64v8 | 3.x | yes | arm64v8/node:18-alpine | 121 | | | 18 | i386 | 3.x | yes | i386/node:18-alpine | 122 | | | | | | | | 123 | | 4.0.9-18-minimal | 18 | amd64 | no | no | amd64/node:18-alpine | 124 | | | 18 | arm32v7 | no | no | arm32v7/node:18-alpine | 125 | | | 18 | arm64v8 | no | no | arm64v8/node:18-alpine | 126 | | | 18 | i386 | no | no | i386/node:18-alpine | 127 | 128 | | **Tag** |**Node**| **Arch** | **Python** |**Dev**| **Base Image** | 129 | |----------------------------|--------|----------|------------|-------|----------------------------| 130 | | 4.0.9-20 | 20 | amd64 | 3.x | yes | amd64/node:20-alpine | 131 | | | 20 | arm32v7 | 3.x | yes | arm32v7/node:20-alpine | 132 | | | 20 | arm64v8 | 3.x | yes | arm64v8/node:20-alpine | 133 | | | 20 | i386 | 3.x | yes | i386/node:20-alpine | 134 | | | | | | | | 135 | | 4.0.9-20-minimal | 20 | amd64 | no | no | amd64/node:20-alpine | 136 | | | 20 | arm32v7 | no | no | arm32v7/node:20-alpine | 137 | | | 20 | arm64v8 | no | no | arm64v8/node:20-alpine | 138 | | | 20 | i386 | no | no | i386/node:20-alpine | 139 | | | | | | | | 140 | | 4.0.9-debian | 20 | amd64 | 3.x | yes | amd64/node:20-buster-slim | 141 | | | 20 | arm32v7 | 3.x | yes | amd64/node:20-buster-slim | 142 | | | 20 | arm64v8 | 3.x | yes | amd64/node:20-buster-slim | 143 | 144 | | **Tag** |**Node**| **Arch** | **Python** |**Dev**| **Base Image** | 145 | |----------------------------|--------|----------|------------|-------|----------------------------| 146 | | 4.0.9-22 | 22 | amd64 | 3.x | yes | amd64/node:22-alpine | 147 | | | 22 | arm32v7 | 3.x | yes | arm32v7/node:22-alpine | 148 | | | 22 | arm64v8 | 3.x | yes | arm64v8/node:22-alpine | 149 | | | 22 | i386 | 3.x | yes | i386/node:22-alpine | 150 | | | | | | | | 151 | | 4.0.9-22-minimal | 22 | amd64 | no | no | amd64/node:22-alpine | 152 | | | 22 | arm32v7 | no | no | arm32v7/node:22-alpine | 153 | | | 22 | arm64v8 | no | no | arm64v8/node:22-alpine | 154 | | | 22 | i386 | no | no | i386/node:22-alpine | 155 | 156 | - All images have bash, tzdata, nano, curl, git, openssl and openssh-client pre-installed to support Node-RED's Projects feature. 157 | 158 | ## Manifest Lists 159 | The following table shows the provided Manifest Lists. 160 | 161 | | **Tag** | **Node-RED Base Image** | 162 | |----------------------------------------|--------------------------------------------| 163 | | latest, 4.0.9, | nodered/node-red:4.0.9-20 | 164 | | latest-20, 4.0.9-20 | | 165 | | | | 166 | | | | 167 | | latest-minimal, 4.0.9-minimal, | nodered/node-red:4.0.9-20-minimal | 168 | | latest-20-minimal, 4.0.9-20-minimal | | 169 | | | | 170 | | latest-debian | nodered/node-red:latest-debian | 171 | 172 | 173 | | **Tag** | **Node-RED Base Image** | 174 | |----------------------------------------|--------------------------------------------| 175 | | latest-18, 4.0.9-18 | nodered/node-red:4.0.9-18 | 176 | | | | 177 | | latest-18-minimal, 4.0.9-18-minimal | nodered/node-red:4.0.9-18-minimal | 178 | 179 | 180 | | **Tag** | **Node-RED Base Image** | 181 | |----------------------------------------|--------------------------------------------| 182 | | latest-22, 4.0.9-22 | nodered/node-red:4.0.9-22 | 183 | | | | 184 | | latest-22-minimal, 4.0.9-22-minimal | nodered/node-red:4.0.9-22-minimal 185 | 186 | 187 | With the support of Docker manifest list, there is no need to explicitly add the tag for the architecture to use. 188 | When a docker run command or docker service command or docker stack command is executed, docker checks which architecture is required and verifies if it is available in the docker repository. If it does, docker pulls the matching image for it. 189 | 190 | Therefore all tags regarding Raspberry PI's are dropped. 191 | 192 | For example: suppose you are running on a Raspberry PI 3B, which has `arm32v7` as architecture. Then just run the following command to pull the image (tagged by `4.0.9-20`), and run the container. 193 | 194 | 195 | ``` 196 | docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered nodered/node-red:latest 197 | ``` 198 | 199 | This gives the advantage that you don't need to know/specify which architecture you are running on and makes docker run commands and docker compose files more flexible and exchangeable across systems. 200 | 201 | 202 | ## Raspberry PI - native GPIO support 203 | | v1.0 - BREAKING: Native GPIO support for Raspberry PI has been dropped | 204 | | --- | 205 | The replacement for native GPIO is [node-red-node-pi-gpiod](https://github.com/node-red/node-red-nodes/tree/master/hardware/pigpiod). 206 | 207 | Disadvantages of the native GPIO support are: 208 | - Your Docker container needs to be deployed on the same Docker node/host on which you want to control the gpio. 209 | - Gain access to `/dev/mem` of your Docker node/host 210 | - privileged=true is not supported for `docker stack` command 211 | 212 | `node-red-node-pi-gpiod` fixes all these disadvantages. With `node-red-node-pi-gpiod` it is possible to interact with gpio of multiple Raspberry Pi's from a single Node-RED container, and for multiple containers to access different gpio on the same Pi. 213 | 214 | ### Quick Migration steps to `node-red-node-pi-gpiod` 215 | 1. Install `node-red-node-pi-gpiod` through the Node-RED palette 216 | 2. Install and run `PiGPIOd daemon` on the host Pi. 217 | 3. Replace all native gpio nodes with `pi gpiod` nodes. 218 | 4. Configure `pi gpiod` nodes to connect to `PiGPIOd daemon`. Often the host machine will have an IP 172.17.0.1 port 8888 - but not always. You can use `docker exec -it mynodered ip route show default | awk '/default/ {print $3}'` to check. 219 | 220 | For detailed install instruction please refer to the `node-red-node-pi-gpiod` [README](https://github.com/node-red/node-red-nodes/tree/master/hardware/pigpiod#node-red-node-pi-gpiod) 221 | 222 | **Note**: There is a contributed [gpiod project](https://github.com/corbosman/node-red-gpiod) that runs the gpiod in its own container rather than on the host if required. 223 | 224 | ## Managing User Data 225 | 226 | Once you have Node-RED running with Docker, we need to 227 | ensure any added nodes or flows are not lost if the container is destroyed. 228 | This user data can be persisted by mounting a data directory to a volume outside the container. 229 | This can either be done using a bind mount or a named data volume. 230 | 231 | Node-RED uses the `/data` directory inside the container to store user configuration data. 232 | 233 | Depending on how and where you mount the user data directory you may want to turn off the built in healthcheck function by adding `--no-healthcheck` to the run command. 234 | 235 | ### Using a Host Directory for Persistence (Bind Mount) 236 | To save your Node-RED user directory inside the container to a host directory outside the container, you can use the 237 | command below. To allow access to this host directory, the node-red user (default uid=1000) inside the container must 238 | have the same uid as the owner of the host directory. 239 | ``` 240 | docker run -it -p 1880:1880 -v /home/pi/.node-red:/data --name mynodered nodered/node-red 241 | ``` 242 | 243 | In this example the host `/home/pi/.node-red` directory is bound to the container `/data` directory. 244 | 245 | **Note**: Users migrating from version 0.20 to 1.0 will need to ensure that any existing `/data` 246 | directory has the correct ownership. As of 1.0 this needs to be `1000:1000`. This can be forced by 247 | the command `sudo chown -R 1000:1000 path/to/your/node-red/data` 248 | 249 | See [the wiki](https://github.com/node-red/node-red-docker/wiki/Permissions-and-Persistence) for detailed information 250 | on permissions. 251 | 252 | ### Using Named Data Volumes 253 | 254 | Docker also supports using named [data volumes](https://docs.docker.com/engine/tutorials/dockervolumes/) 255 | to store persistent or shared data outside the container. 256 | 257 | To create a new named data volume to persist our user data and run a new 258 | container using this volume. 259 | 260 | $ docker volume create --name node_red_data_vol 261 | $ docker volume ls 262 | DRIVER VOLUME NAME 263 | local node_red_data_vol 264 | $ docker run -it -p 1880:1880 -v node_red_data_vol:/data --name mynodered nodered/node-red 265 | 266 | Using Node-RED to create and deploy some sample flows, we can now destroy the 267 | container and start a new instance without losing our user data. 268 | 269 | $ docker rm mynodered 270 | $ docker run -it -p 1880:1880 -v node_red_data_vol:/data --name mynodered nodered/node-red 271 | 272 | ## Updating 273 | 274 | As the /data is now preserved outside of the container, updating the base container image 275 | is now as simple as 276 | 277 | $ docker pull nodered/node-red 278 | $ docker stop mynodered 279 | $ docker start mynodered 280 | 281 | 282 | ## Docker Stack / Docker Compose 283 | 284 | Below an example of a Docker Compose file which can be run by `docker stack` or `docker-compose`. 285 | Please refer to the official Docker pages for more info about [Docker stack](https://docs.docker.com/engine/reference/commandline/stack/) and [Docker compose](https://docs.docker.com/compose/). 286 | 287 | ``` 288 | ################################################################################ 289 | # Node-RED Stack or Compose 290 | ################################################################################ 291 | # docker stack deploy node-red --compose-file docker-compose-node-red.yml 292 | # docker-compose -f docker-compose-node-red.yml -p myNoderedProject up 293 | ################################################################################ 294 | version: "3.7" 295 | 296 | services: 297 | node-red: 298 | image: nodered/node-red:latest 299 | environment: 300 | - TZ=Europe/Amsterdam 301 | ports: 302 | - "1880:1880" 303 | networks: 304 | - node-red-net 305 | volumes: 306 | - ~/node-red/data:/data 307 | 308 | networks: 309 | node-red-net: 310 | ``` 311 | 312 | The above compose file: 313 | - creates a node-red service 314 | - pulls the latest node-red image 315 | - sets the timezone to Europe/Amsterdam 316 | - Maps the container port 1880 to the the host port 1880 317 | - creates a node-red-net network and attaches the container to this network 318 | - persists the `/data` dir inside the container to the users local `node-red/data` directory. The `node-red/data` directory must exist prior to starting the container. 319 | 320 | ## Project Layout 321 | This repository contains Dockerfiles to build the Node-RED Docker images listed above. 322 | 323 | ### package.json 324 | 325 | The package.json is a metafile that downloads and installs the required version 326 | of Node-RED and any other npms you wish to install at build time. During the 327 | Docker build process, the dependencies are installed under `/usr/src/node-red`. 328 | 329 | The main sections to modify are 330 | 331 | "dependencies": { 332 | "node-red": "^4.0.9", <-- set the version of Node-RED here 333 | "node-red-dashboard": "*" <-- add any extra npm packages here 334 | }, 335 | 336 | This is where you can pre-define any extra nodes you want installed every time 337 | by default, and then 338 | 339 | "scripts" : { 340 | "start": "node-red -v $FLOWS" 341 | }, 342 | 343 | This is the command that starts Node-RED when the container is run. 344 | 345 | ### Startup 346 | 347 | Node-RED is started using NPM start from this `/usr/src/node-red`, with the `--userDir` 348 | parameter pointing to the `/data` directory on the container. 349 | 350 | The flows configuration file is set using an environment parameter (**FLOWS**), 351 | which defaults to *'flows.json'*. This can be changed at runtime using the 352 | following command-line flag. 353 | ``` 354 | docker run -it -p 1880:1880 -e FLOWS=my_flows.json -v node_red_data:/data nodered/node-red 355 | ``` 356 | 357 | **Note**: If you set `-e FLOWS=""` then the flow file can be set via the *flowFile* 358 | property in the `settings.js` file. 359 | 360 | Node.js runtime arguments can be passed to the container using an environment 361 | parameter (**NODE_OPTIONS**). For example, to fix the heap size used by 362 | the Node.js garbage collector you would use the following command. 363 | ``` 364 | docker run -it -p 1880:1880 -e NODE_OPTIONS="--max_old_space_size=128" -v node_red_data:/data nodered/node-red 365 | ``` 366 | 367 | Other useful environment variables include 368 | 369 | - -e NODE_RED_ENABLE_SAFE_MODE=false # setting to true starts Node-RED in safe (not running) mode 370 | - -e NODE_RED_ENABLE_PROJECTS=false # setting to true starts Node-RED with the projects feature enabled 371 | 372 | 373 | ### Node-RED Admin Tool 374 | 375 | Using the administration tool, with port forwarding on the container to the host 376 | system, extra nodes can be installed without leaving the host system. 377 | 378 | $ npm install -g node-red-admin 379 | $ node-red-admin install node-red-node-openwhisk 380 | 381 | This tool assumes Node-RED is available at the following address 382 | `http://localhost:1880`. 383 | 384 | Refreshing the browser page should now reveal the newly added node in the palette. 385 | 386 | ### Node-RED Commands from the host 387 | 388 | Admin commands can also be accessed without installing npm or the 389 | node-red-admin tool on the host machine. Simply prepend your command 390 | with "npx" and apply it to the container - e.g 391 | 392 | $ docker exec -it mynodered npx node-red admin hash-pw 393 | 394 | ### Container Shell 395 | 396 | $ docker exec -it mynodered /bin/bash 397 | 398 | Will give a command line inside the container - where you can then run the npm install 399 | command you wish - e.g. 400 | 401 | $ cd /data 402 | $ npm install node-red-node-smooth 403 | $ exit 404 | $ docker stop mynodered 405 | $ docker start mynodered 406 | 407 | Refreshing the browser page should now reveal the newly added node in the palette. 408 | 409 | ### Building Custom Image 410 | 411 | Creating a new Docker image, using the public Node-RED images as the base image, 412 | allows you to install extra nodes during the build process. 413 | 414 | This Dockerfile builds a custom Node-RED image with the flightaware module 415 | installed from NPM. 416 | 417 | ``` 418 | FROM nodered/node-red 419 | RUN npm install node-red-contrib-flightaware 420 | ``` 421 | 422 | Alternatively, you can modify the package.json in this repository and re-build 423 | the images from scratch. This will also allow you to modify the version of 424 | Node-RED that is installed. See [README](docker-custom/README.md) in the `docker-custom` directory. 425 | 426 | ## Running headless 427 | 428 | The barest minimum we need to just run Node-RED is 429 | 430 | $ docker run -d -p 1880:1880 nodered/node-red 431 | 432 | This will create a local running instance of a machine - that will have some 433 | docker id number and be running on a random port... to find out run 434 | 435 | $ docker ps 436 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 437 | 4bbeb39dc8dc nodered/node-red:latest "npm start" 4 seconds ago Up 4 seconds 0.0.0.0:49154->1880/tcp furious_yalow 438 | $ 439 | 440 | You can now point a browser to the host machine on the tcp port reported back, so in the example 441 | above browse to `http://{host ip}:49154` 442 | 443 | **NOTE**: as this does not mount the `/data` volume externally any changes to flows will not be saved and if the container is redeployed or upgraded these will be lost. The volume may persist on the host filing sysem and can probably be retrieved and remounted if required. 444 | 445 | ## Linking Containers 446 | 447 | You can link containers "internally" within the docker runtime by using Docker [user-defined bridges](https://docs.docker.com/network/bridge/). 448 | 449 | Before using a bridge, it needs to be created. The command below will create a new bridge called **iot** 450 | 451 | docker network create iot 452 | 453 | Then all containers that need to communicate need to be added to the same bridge using the **--network** command line option 454 | 455 | docker run -itd --network iot --name mybroker eclipse-mosquitto mosquitto -c /mosquitto-no-auth.conf 456 | 457 | (no need to expose the port 1883 globally unless you want to... as we do magic below) 458 | 459 | Then run nodered docker, also added to the same bridge 460 | 461 | docker run -itd -p 1880:1880 --network iot --name mynodered nodered/node-red 462 | 463 | containers on the same user-defined bridge can take advantage of the built in name resolution provided by the bridge and use the container name (specified using the **--name** option) as the target hostname. 464 | 465 | 466 | In the above example the broker can be reached from the Node-RED application using hostname *mybroker*. 467 | 468 | Then a simple flow like below show the mqtt nodes connecting to the broker 469 | 470 | [{"id":"c51cbf73.d90738","type":"mqtt in","z":"3fa278ec.8cbaf","name":"","topic":"test","broker":"5673f1d5.dd5f1","x":290,"y":240,"wires":[["7781c73.639b8b8"]]},{"id":"7008d6ef.b6ee38","type":"mqtt out","z":"3fa278ec.8cbaf","name":"","topic":"test","qos":"","retain":"","broker":"5673f1d5.dd5f1","x":517,"y":131,"wires":[]},{"id":"ef5b970c.7c864","type":"inject","z":"3fa278ec.8cbaf","name":"","repeat":"","crontab":"","once":false,"topic":"","payload":"","payloadType":"date","x":290,"y":153,"wires":[["7008d6ef.b6ee38"]]},{"id":"7781c73.639b8b8","type":"debug","z":"3fa278ec.8cbaf","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":505,"y":257,"wires":[]},{"id":"5673f1d5.dd5f1","type":"mqtt-broker","z":"","name":"","broker":"mybroker","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"15","cleansession":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","closeTopic":"","closeRetain":"false","closePayload":"","willTopic":"","willQos":"0","willRetain":"false","willPayload":""}] 471 | 472 | This way the internal broker is not exposed outside of the docker host - of course 473 | you may add `-p 1883:1883` etc to the broker run command if you want other systems outside your computer to be able to use the broker. 474 | 475 | ### Docker-Compose linking example 476 | 477 | Another way to link containers is by using docker-compose. The following docker-compose.yml 478 | file creates a Node-RED instance, and a local MQTT broker instance. In the Node-RED flow the broker can be addressed simply as `mybroker` at its default port `1883`. 479 | 480 | ``` 481 | version: "3.7" 482 | 483 | services: 484 | mynodered: 485 | image: nodered/node-red 486 | restart: unless-stopped 487 | volumes: 488 | - /home/pi/.node-red:/data 489 | ports: 490 | - 1880:1880 491 | mybroker: 492 | image: eclipse-mosquitto 493 | restart: unless-stopped 494 | command: mosquitto -c /mosquitto-no-auth.conf 495 | ``` 496 | 497 | ## Debugging containers 498 | 499 | Sometimes it is useful to debug the code which is running inside the container. Two scripts (*'debug'* and *'debug_brk'* in the package.json file) are available to start NodeJs in debug mode, which means that NodeJs will start listening (to port 9229) for a debug client. Various remote debugger tools (like Visual Code, Chrome Developer Tools ...) can be used to debug a Node-RED application. A [wiki](https://github.com/node-red/node-red-docker/wiki/Debug-container-via-Chrome-Developer-Tools) page has been provided, to explain step-by-step how to use the Chrome Developer Tools debugger. 500 | 501 | 1. In most cases the *'debug'* script will be sufficient, to debug a Node-RED application that is fully up-and-running (i.e. when the application startup code is not relevant). The NodeJs server can be started in debug mode using following command: 502 | ``` 503 | docker run -it -p 1880:1880 -p 9229:9229 -v node_red_data:/data --name mynodered --entrypoint npm nodered/node-red run debug -- --userDir /data 504 | ``` 505 | 506 | 2. In case debugging of the Node-RED startup code is required, the *'debug_brk'* script will instruct NodeJs to break at the first statement of the Node-RED application. The NodeJs server can be started in debug mode using following command: 507 | ``` 508 | docker run -it -p 1880:1880 -p 9229:9229 -v node_red_data:/data --name mynodered --entrypoint npm nodered/node-red run debug_brk -- --userDir /data 509 | ``` 510 | Note that in this case NodeJs will wait - at the first statement of the Node-RED application - until a debugger client connects... 511 | 512 | As soon as NodeJs is listening to the debug port, this will be shown in the startup log: 513 | ``` 514 | Debugger listening on ws://0.0.0.0:9229/... 515 | ``` 516 | 517 | Let's dissect both commands: 518 | 519 | docker run - run this container, initially building locally if necessary 520 | -it - attach a terminal session so we can see what is going on 521 | -p 1880:1880 - connect local port 1880 to the exposed internal port 1880 522 | -p 9229:9229 - connect local port 9229 to the exposed internal port 9229 (for debugger communication) 523 | -v node_red_data:/data - mount the internal /data to the host mode_red_data directory 524 | --name mynodered - give this machine a friendly local name 525 | --entrypoint npm - overwrite the default entrypoint (which would run the *'start'* script) 526 | nodered/node-red - the image to base it on - currently Node-RED v1.1.0 527 | run debug(_brk) - (npm) arguments for the custom endpoint (which must be added AFTER the image name!) 528 | -- - the arguments that will follow are not npm arguments, but need to be passed to the script 529 | --userDir /data - instruct the script where the Node-RED data needs to be stored 530 | 531 | ## Common Issues and Hints 532 | 533 | Here is a list of common issues users have reported with possible solutions. 534 | 535 |
536 | 537 | ### User Permission Errors 538 | 539 | See [the wiki](https://github.com/node-red/node-red-docker/wiki/Permissions-and-Persistence) for detailed information 540 | on permissions. 541 | 542 | If you are seeing *permission denied* errors opening files or accessing host devices, try running the container as the root user. 543 | 544 | ``` 545 | docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered -u root nodered/node-red 546 | ``` 547 | 548 | __References:__ 549 | 550 | https://github.com/node-red/node-red/issues/15 551 | 552 | https://github.com/node-red/node-red/issues/8 553 | 554 |
555 | 556 | ### Accessing Host Devices 557 | 558 | If you want to access a device from the host inside the container, e.g. serial port, use the following command-line flag to pass access through. 559 | 560 | ``` 561 | docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered --device=/dev/ttyACM0 nodered/node-red 562 | ``` 563 | __References:__ 564 | 565 | https://github.com/node-red/node-red/issues/15 566 | 567 |
568 | 569 | ### Setting Timezone 570 | 571 | If you want to modify the default timezone, use the TZ environment variable with the [relevant timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). 572 | 573 | ``` 574 | docker run -it -p 1880:1880 -v node_red_data:/data --name mynodered -e TZ=America/New_York nodered/node-red 575 | ``` 576 | 577 | or within a docker-compose file 578 | ``` 579 | node-red: 580 | environment: 581 | - TZ=America/New_York 582 | ``` 583 | 584 | __References:__ 585 | 586 | https://groups.google.com/forum/#!topic/node-red/ieo5IVFAo2o 587 | 588 |
589 | -------------------------------------------------------------------------------- /docker-custom/Dockerfile.custom: -------------------------------------------------------------------------------- 1 | ARG ARCH=amd64 2 | ARG NODE_VERSION=20 3 | ARG OS=alpine 4 | 5 | #### Stage BASE ######################################################################################################## 6 | FROM ${ARCH}/node:${NODE_VERSION}-${OS} AS base 7 | 8 | # Copy scripts 9 | COPY scripts/*.sh /tmp/ 10 | COPY healthcheck.js / 11 | 12 | # Install tools, create Node-RED app and data dir, add user and set rights 13 | RUN set -ex && \ 14 | apk add --no-cache \ 15 | bash \ 16 | tzdata \ 17 | iputils \ 18 | curl \ 19 | nano \ 20 | git \ 21 | openssl \ 22 | openssh-client \ 23 | ca-certificates && \ 24 | mkdir -p /usr/src/node-red /data && \ 25 | deluser --remove-home node && \ 26 | adduser -h /usr/src/node-red -D -H node-red -u 1000 && \ 27 | chown -R node-red:root /data && chmod -R g+rwX /data && \ 28 | chown -R node-red:root /usr/src/node-red && chmod -R g+rwX /usr/src/node-red 29 | # chown -R node-red:node-red /data && \ 30 | # chown -R node-red:node-red /usr/src/node-red 31 | 32 | # Set work directory 33 | WORKDIR /usr/src/node-red 34 | 35 | # Setup SSH known_hosts file 36 | COPY known_hosts.sh . 37 | RUN ./known_hosts.sh /etc/ssh/ssh_known_hosts && rm /usr/src/node-red/known_hosts.sh 38 | RUN echo "PubkeyAcceptedKeyTypes +ssh-rsa" >> /etc/ssh/ssh_config 39 | 40 | # package.json contains Node-RED NPM module and node dependencies 41 | COPY package.json . 42 | COPY flows.json /data 43 | COPY scripts/entrypoint.sh . 44 | 45 | #### Stage BUILD ####################################################################################################### 46 | FROM base AS build 47 | 48 | # Install Build tools 49 | RUN apk add --no-cache --virtual buildtools build-base linux-headers udev python3 && \ 50 | npm install --unsafe-perm --no-update-notifier --no-fund --only=production && \ 51 | /tmp/remove_native_gpio.sh && \ 52 | cp -R node_modules prod_node_modules 53 | 54 | #### Stage RELEASE ##################################################################################################### 55 | FROM base AS release 56 | ARG BUILD_DATE 57 | ARG BUILD_VERSION 58 | ARG BUILD_REF 59 | ARG NODE_RED_VERSION 60 | ARG ARCH 61 | ARG TAG_SUFFIX=default 62 | 63 | LABEL org.label-schema.build-date=${BUILD_DATE} \ 64 | org.label-schema.docker.dockerfile=".docker/Dockerfile.alpine" \ 65 | org.label-schema.license="Apache-2.0" \ 66 | org.label-schema.name="Node-RED" \ 67 | org.label-schema.version=${BUILD_VERSION} \ 68 | org.label-schema.description="Low-code programming for event-driven applications." \ 69 | org.label-schema.url="https://nodered.org" \ 70 | org.label-schema.vcs-ref=${BUILD_REF} \ 71 | org.label-schema.vcs-type="Git" \ 72 | org.label-schema.vcs-url="https://github.com/node-red/node-red-docker" \ 73 | org.opencontainers.image.source="https://github.com/node-red/node-red-docker" \ 74 | org.label-schema.arch=${ARCH} \ 75 | authors="Dave Conway-Jones, Nick O'Leary, James Thomas, Raymond Mouthaan" 76 | 77 | COPY --from=build /usr/src/node-red/prod_node_modules ./node_modules 78 | 79 | # Chown, install devtools & Clean up 80 | RUN chown -R node-red:root /usr/src/node-red && \ 81 | /tmp/install_devtools.sh && \ 82 | rm -r /tmp/* 83 | 84 | RUN npm config set cache /data/.npm --global 85 | 86 | USER node-red 87 | 88 | # Env variables 89 | ENV NODE_RED_VERSION=$NODE_RED_VERSION \ 90 | NODE_PATH=/usr/src/node-red/node_modules:/data/node_modules \ 91 | PATH=/usr/src/node-red/node_modules/.bin:${PATH} \ 92 | FLOWS=flows.json 93 | 94 | # ENV NODE_RED_ENABLE_SAFE_MODE=true # Uncomment to enable safe start mode (flows not running) 95 | # ENV NODE_RED_ENABLE_PROJECTS=true # Uncomment to enable projects option 96 | 97 | # Expose the listening port of node-red 98 | EXPOSE 1880 99 | 100 | # Add a healthcheck (default every 30 secs) 101 | HEALTHCHECK CMD node /healthcheck.js 102 | 103 | # ENTRYPOINT ["npm", "start", "--cache", "/data/.npm", "--", "--userDir", "/data"] 104 | ENTRYPOINT ["./entrypoint.sh"] 105 | -------------------------------------------------------------------------------- /docker-custom/Dockerfile.debian: -------------------------------------------------------------------------------- 1 | ARG ARCH=amd64 2 | ARG NODE_VERSION=20 3 | ARG OS=bullseye-slim 4 | 5 | #### Stage BASE ######################################################################################################## 6 | FROM ${ARCH}/node:${NODE_VERSION}-${OS} AS base 7 | 8 | # Copy scripts 9 | COPY scripts/*.sh /tmp/ 10 | COPY healthcheck.js / 11 | 12 | # Install tools, create Node-RED app and data dir, add user and set rights 13 | RUN set -ex && \ 14 | apt-get update && apt-get install -y \ 15 | bash \ 16 | tzdata \ 17 | curl \ 18 | nano \ 19 | wget \ 20 | git \ 21 | openssl \ 22 | openssh-client \ 23 | ca-certificates \ 24 | iputils-ping && \ 25 | mkdir -p /usr/src/node-red /data && \ 26 | deluser --remove-home node && \ 27 | # adduser --home /usr/src/node-red --disabled-password --no-create-home node-red --uid 1000 && \ 28 | useradd --home-dir /usr/src/node-red --uid 1000 node-red && \ 29 | chown -R node-red:root /data && chmod -R g+rwX /data && \ 30 | chown -R node-red:root /usr/src/node-red && chmod -R g+rwX /usr/src/node-red 31 | # chown -R node-red:node-red /data && \ 32 | # chown -R node-red:node-red /usr/src/node-red 33 | 34 | # Set work directory 35 | WORKDIR /usr/src/node-red 36 | 37 | # Setup SSH known_hosts file 38 | COPY known_hosts.sh . 39 | RUN ./known_hosts.sh /etc/ssh/ssh_known_hosts && rm /usr/src/node-red/known_hosts.sh 40 | RUN echo "PubkeyAcceptedKeyTypes +ssh-rsa" >> /etc/ssh/ssh_config 41 | 42 | # package.json contains Node-RED NPM module and node dependencies 43 | COPY package.json . 44 | COPY flows.json /data 45 | COPY scripts/entrypoint.sh . 46 | 47 | #### Stage BUILD ####################################################################################################### 48 | FROM base AS build 49 | 50 | # Install Build tools 51 | RUN apt-get update && apt-get install -y build-essential python && \ 52 | npm install --unsafe-perm --no-update-notifier --no-fund --only=production && \ 53 | npm uninstall node-red-node-gpio && \ 54 | cp -R node_modules prod_node_modules 55 | 56 | #### Stage RELEASE ##################################################################################################### 57 | FROM base AS release 58 | ARG BUILD_DATE 59 | ARG BUILD_VERSION 60 | ARG BUILD_REF 61 | ARG NODE_RED_VERSION 62 | ARG ARCH 63 | ARG TAG_SUFFIX=default 64 | 65 | LABEL org.label-schema.build-date=${BUILD_DATE} \ 66 | org.label-schema.docker.dockerfile=".docker/Dockerfile.debian" \ 67 | org.label-schema.license="Apache-2.0" \ 68 | org.label-schema.name="Node-RED" \ 69 | org.label-schema.version=${BUILD_VERSION} \ 70 | org.label-schema.description="Low-code programming for event-driven applications." \ 71 | org.label-schema.url="https://nodered.org" \ 72 | org.label-schema.vcs-ref=${BUILD_REF} \ 73 | org.label-schema.vcs-type="Git" \ 74 | org.label-schema.vcs-url="https://github.com/node-red/node-red-docker" \ 75 | org.opencontainers.image.source="https://github.com/node-red/node-red-docker" \ 76 | org.label-schema.arch=${ARCH} \ 77 | authors="Dave Conway-Jones, Nick O'Leary, James Thomas, Raymond Mouthaan" 78 | 79 | COPY --from=build /usr/src/node-red/prod_node_modules ./node_modules 80 | 81 | # Chown, install devtools & Clean up 82 | RUN chown -R node-red:root /usr/src/node-red && \ 83 | apt-get update && apt-get install -y build-essential python-dev python3 && \ 84 | rm -r /tmp/* 85 | 86 | RUN npm config set cache /data/.npm --global 87 | 88 | USER node-red 89 | 90 | # Env variables 91 | ENV NODE_RED_VERSION=$NODE_RED_VERSION \ 92 | NODE_PATH=/usr/src/node-red/node_modules:/data/node_modules \ 93 | PATH=/usr/src/node-red/node_modules/.bin:${PATH} \ 94 | FLOWS=flows.json 95 | 96 | # ENV NODE_RED_ENABLE_SAFE_MODE=true # Uncomment to enable safe start mode (flows not running) 97 | # ENV NODE_RED_ENABLE_PROJECTS=true # Uncomment to enable projects option 98 | 99 | # Expose the listening port of node-red 100 | EXPOSE 1880 101 | 102 | # Add a healthcheck (default every 30 secs) 103 | HEALTHCHECK CMD node /healthcheck.js 104 | 105 | ENTRYPOINT ["./entrypoint.sh"] 106 | -------------------------------------------------------------------------------- /docker-custom/README.md: -------------------------------------------------------------------------------- 1 | # Build your own Docker image 2 | 3 | The docker-custom directory contains files you need to build your own images. 4 | 5 | The follow steps describe in short which steps to take to build your own images. 6 | 7 | ## 1. git clone 8 | 9 | Clone the Node-RED Docker project from github 10 | ```shell script 11 | git clone https://github.com/node-red/node-red-docker.git 12 | ``` 13 | 14 | Change dir to docker-custom 15 | ```shell script 16 | cd node-red-docker/docker-custom 17 | ``` 18 | 19 | ## 1. **package.json** 20 | 21 | - Change the node-red version in package.json (from the docker-custom directory) to the version you require 22 | - Add optionally packages you require 23 | 24 | ## 2. **flows.json** 25 | 26 | - The `flows.json` file is the default flow that will be used if no external volume is mounted to `/data`. You can replace this by a preconfigured flow and launch it by not mounting a /data volume, but most users will mount and save data and flows externally. 27 | 28 | ## 3. **docker-alpine.sh, docker-debian.sh** 29 | 30 | The `docker-alpine.sh` and `docker-debian.sh` are helper scripts to build a custom Node-RED docker image. The docker-alpine script is based on Alpine as per the default docker package. The docker-debian is based on debian that may be more familiar to users and may support extra customisation more easily. 31 | 32 | Change the build arguments as needed: 33 | 34 | - `--build-arg ARCH=amd64` : architecture your are building for (arm32v6, arm32v7, arm64v8, amd64) 35 | - `--build-arg NODE_VERSION=20` : NodeJS version you like to use 36 | - `--build-arg NODE_RED_VERSION=${NODE_RED_VERSION}` : don't change this, ${NODE_RED_VERSION} gets populated from package.json 37 | - `--build-arg OS=alpine` : the linux distro to use (alpine) 38 | - `--build-arg BUILD_DATE="$(date +"%Y-%m-%dT%H:%M:%SZ")"` : don't change this 39 | - `--build-arg TAG_SUFFIX=default` : to build the default or minimal image (only applies to the alpine build) 40 | - `--file Dockerfile.custom` : Dockerfile to use to build your image. 41 | - `--tag testing:node-red-build` : set the image name and tag 42 | 43 | ## 4. **Run docker-alpine.sh** or **docker-debian.sh** 44 | 45 | Run `docker-alpine.sh` or `docker-debian.sh` 46 | 47 | ```shell script 48 | $ ./docker-alpine.sh 49 | ``` 50 | 51 | This starts building your custom image and might take a while depending on the system you are running on. 52 | 53 | When building is done you can run the custom image by the following command: 54 | 55 | ```shell script 56 | $ docker run -it -p1880:1880 -v node_red_data:/data --name myNRtest testing:node-red-build 57 | ``` 58 | 59 | With the following command you can verify your docker image: 60 | 61 | ```shell script 62 | $ docker inspect testing:node-red-build 63 | ``` 64 | 65 | ## 5. **Advanced Configuration** 66 | 67 | The relevant `Dockerfile` can be modified as required. Examples of how to extend `Dockerfiles` to add prerequisite libraries can be found [here](https://nodered.org/docs/getting-started/docker-custom). 68 | 69 | -------------------------------------------------------------------------------- /docker-custom/docker-alpine.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export NODE_RED_VERSION=$(grep -oE "\"node-red\": \"(\w*.\w*.\w*.\w*.\w*.)" package.json | cut -d\" -f4) 3 | 4 | echo "#########################################################################" 5 | echo "node-red version: ${NODE_RED_VERSION}" 6 | echo "#########################################################################" 7 | 8 | docker build --rm --no-cache \ 9 | --build-arg ARCH=amd64 \ 10 | --build-arg NODE_VERSION=20 \ 11 | --build-arg NODE_RED_VERSION=${NODE_RED_VERSION} \ 12 | --build-arg OS=alpine \ 13 | --build-arg BUILD_DATE="$(date +"%Y-%m-%dT%H:%M:%SZ")" \ 14 | --build-arg TAG_SUFFIX=default \ 15 | --file Dockerfile.custom \ 16 | --tag testing:node-red-build . 17 | -------------------------------------------------------------------------------- /docker-custom/docker-debian.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export NODE_RED_VERSION=$(grep -oE "\"node-red\": \"(\w*.\w*.\w*.\w*.\w*.)" package.json | cut -d\" -f4) 3 | 4 | echo "#########################################################################" 5 | echo "node-red version: ${NODE_RED_VERSION}" 6 | echo "#########################################################################" 7 | 8 | docker build --rm --no-cache \ 9 | --build-arg ARCH=amd64 \ 10 | --build-arg NODE_VERSION=20 \ 11 | --build-arg NODE_RED_VERSION=${NODE_RED_VERSION} \ 12 | --build-arg OS=bullseye-slim \ 13 | --build-arg BUILD_DATE="$(date +"%Y-%m-%dT%H:%M:%SZ")" \ 14 | --build-arg TAG_SUFFIX=default \ 15 | --file Dockerfile.debian \ 16 | --tag testing:node-red-build . 17 | -------------------------------------------------------------------------------- /docker-custom/flows.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "f6f2187d.f17ca8", 4 | "type": "tab", 5 | "label": "Flow 1", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "3cc11d24.ff01a2", 11 | "type": "comment", 12 | "z": "f6f2187d.f17ca8", 13 | "name": "WARNING: please check you have started this container with a volume that is mounted to /data\\n otherwise any flow changes are lost when you redeploy or upgrade the container\\n (e.g. upgrade to a more recent node-red docker image).\\n If you are using named volumes you can ignore this warning.\\n Double click or see info side panel to learn how to start Node-RED in Docker to save your work", 14 | "info": "\nTo start docker with a bind mount volume (-v option), for example:\n\n```\ndocker run -it -p 1880:1880 -v /home/user/node_red_data:/data --name mynodered nodered/node-red\n```\n\nwhere `/home/user/node_red_data` is a directory on your host machine where you want to store your flows.\n\nIf you do not do this then you can experiment and redploy flows, but if you restart or upgrade the container the flows will be disconnected and lost. \n\nThey will still exist in a hidden data volume, which can be recovered using standard docker techniques, but that is much more complex than just starting with a named volume as described above.", 15 | "x": 350, 16 | "y": 80, 17 | "wires": [] 18 | } 19 | ] -------------------------------------------------------------------------------- /docker-custom/healthcheck.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var https = require('https'); 3 | var settings = require('/data/settings.js'); 4 | var request; 5 | 6 | process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0; 7 | 8 | var options = { 9 | host : "127.0.0.1", 10 | port : settings.uiPort || 1880, 11 | timeout : 4000 12 | }; 13 | 14 | if (settings.hasOwnProperty("https")) { 15 | request = https.request(options, (res) => { 16 | //console.log(`STATUS: ${res.statusCode}`); 17 | if ((res.statusCode >= 200) && (res.statusCode < 500)) { process.exit(0); } 18 | else { process.exit(1); } 19 | }); 20 | } 21 | else { 22 | request = http.request(options, (res) => { 23 | //console.log(`STATUS: ${res.statusCode}`); 24 | if ((res.statusCode >= 200) && (res.statusCode < 500)) { process.exit(0); } 25 | else { process.exit(1); } 26 | }); 27 | } 28 | 29 | request.on('error', function(err) { 30 | //console.log('ERROR',err); 31 | process.exit(1); 32 | }); 33 | 34 | request.end(); 35 | -------------------------------------------------------------------------------- /docker-custom/known_hosts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Originally taken from the Flux project (https://github.com/fluxcd/flux/tree/master/docker) where is under an 4 | # Apache-2.0 license 5 | 6 | set -eu 7 | 8 | known_hosts_file=${1} 9 | known_hosts_file=${known_hosts_file:-/etc/ssh/ssh_known_hosts} 10 | hosts="github.com gitlab.com bitbucket.org ssh.dev.azure.com vs-ssh.visualstudio.com" 11 | hosts_2022="source.developers.google.com" 12 | 13 | # The heredoc below was generated by constructing a known_hosts using 14 | # 15 | # ssh-keyscan github.com gitlab.com bitbucket.org ssh.dev.azure.com vs-ssh.visualstudio.com > ./known_hosts 16 | # 17 | # then generating the sorted fingerprints with 18 | # 19 | # ssh-keygen -l -f ./known_hosts | LC_ALL=C sort 20 | # 21 | # then checking against the published fingerprints from: 22 | # - github.com: https://help.github.com/articles/github-s-ssh-key-fingerprints/ 23 | # - gitlab.com: https://docs.gitlab.com/ee/user/gitlab_com/#ssh-host-keys-fingerprints 24 | # - bitbucket.org: https://confluence.atlassian.com/bitbucket/ssh-keys-935365775.html 25 | # - ssh.dev.azure.com & vs-ssh.visualstudio.com: sign in, then go to User settings -> SSH Public Keys 26 | # (this is where the public key fingerprint is shown; it's not a setting) 27 | # - source.developers.google.com: https://cloud.google.com/source-repositories/docs/cloning-repositories 28 | 29 | fingerprints=$(mktemp -t) 30 | cleanup() { 31 | rm -f "$fingerprints" 32 | } 33 | trap cleanup EXIT 34 | 35 | # make sure sorting is in the same locale as the heredoc 36 | export LC_ALL=C 37 | 38 | generate() { 39 | ssh-keyscan ${hosts} > ${known_hosts_file} 40 | ssh-keyscan -p 2022 ${hosts_2022} >> ${known_hosts_file} 41 | } 42 | 43 | validate() { 44 | ssh-keygen -l -f ${known_hosts_file} | sort > "$fingerprints" 45 | 46 | diff - "$fingerprints" <=18" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /docker-custom/scripts/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | trap stop SIGINT SIGTERM 4 | 5 | function stop() { 6 | kill $CHILD_PID 7 | wait $CHILD_PID 8 | } 9 | 10 | /usr/local/bin/node $NODE_OPTIONS node_modules/node-red/red.js --userDir /data $FLOWS "${@}" & 11 | 12 | CHILD_PID="$!" 13 | 14 | wait "${CHILD_PID}" 15 | -------------------------------------------------------------------------------- /docker-custom/scripts/install_devtools.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | # Installing Devtools 5 | if [[ ${TAG_SUFFIX} != *"minimal" ]]; then 6 | echo "Installing devtools" 7 | apk add --no-cache --virtual devtools build-base linux-headers udev python3 8 | else 9 | echo "Skip installing devtools" 10 | fi 11 | -------------------------------------------------------------------------------- /docker-custom/scripts/remove_native_gpio.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | # Remove native GPIO node if exists 5 | if [[ -d "/usr/src/node-red/node_modules/@node-red/nodes/core/hardware" ]]; then 6 | echo "Removing native GPIO node" 7 | rm -r /usr/src/node-red/node_modules/@node-red/nodes/core/hardware 8 | else 9 | echo "Skip removing native GPIO node" 10 | fi 11 | -------------------------------------------------------------------------------- /flows.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "f6f2187d.f17ca8", 4 | "type": "tab", 5 | "label": "Flow 1", 6 | "disabled": false, 7 | "info": "" 8 | }, 9 | { 10 | "id": "3cc11d24.ff01a2", 11 | "type": "comment", 12 | "z": "f6f2187d.f17ca8", 13 | "name": "WARNING: please check you have started this container with a volume that is mounted to /data\\n otherwise any flow changes are lost when you redeploy or upgrade the container\\n (e.g. upgrade to a more recent node-red docker image).\\n If you are using named volumes you can ignore this warning.\\n Double click or see info side panel to learn how to start Node-RED in Docker to save your work", 14 | "info": "\nTo start docker with a bind mount volume (-v option), for example:\n\n```\ndocker run -it -p 1880:1880 -v /home/user/node_red_data:/data --name mynodered nodered/node-red\n```\n\nwhere `/home/user/node_red_data` is a directory on your host machine where you want to store your flows.\n\nIf you do not do this then you can experiment and redploy flows, but if you restart or upgrade the container the flows will be disconnected and lost. \n\nThey will still exist in a hidden data volume, which can be recovered using standard docker techniques, but that is much more complex than just starting with a named volume as described above.", 15 | "x": 350, 16 | "y": 80, 17 | "wires": [] 18 | } 19 | ] -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-red-docker", 3 | "version": "4.0.9", 4 | "description": "Low-code programming for event-driven applications", 5 | "homepage": "http://nodered.org", 6 | "license": "Apache-2.0", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/node-red/node-red-docker.git" 10 | }, 11 | "private": true, 12 | "scripts": { 13 | "start": "node $NODE_OPTIONS node_modules/node-red/red.js $FLOWS", 14 | "debug": "node --inspect=0.0.0.0:9229 $NODE_OPTIONS node_modules/node-red/red.js $FLOWS", 15 | "debug_brk": "node --inspect=0.0.0.0:9229 --inspect-brk $NODE_OPTIONS node_modules/node-red/red.js $FLOWS" 16 | }, 17 | "contributors": [ 18 | { 19 | "name": "Dave Conway-Jones" 20 | }, 21 | { 22 | "name": "Nick O'Leary" 23 | }, 24 | { 25 | "name": "James Thomas" 26 | }, 27 | { 28 | "name": "Raymond Mouthaan" 29 | } 30 | ], 31 | "dependencies": { 32 | "node-red": "4.0.9" 33 | }, 34 | "engines": { 35 | "node": ">=18" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /volumechanges.md: -------------------------------------------------------------------------------- 1 | ## Resolution for issue #197 _"docker image always creates a /data volume"_ 2 | 3 | ### Introduction 4 | 5 | This page describes the resolution of issue [#197 docker image always creates a /data volume](https://github.com/node-red/node-red-docker/issues/197) and potential impact. It must be noted that most of us won't see any impact and that the new Node-RED docker images with respect to docker volumes will behave as expected. 6 | 7 | ### Change = removal of line: "`VOLUME [/data]`" 8 | 9 | The Node-RED docker images at [nodered/node-red](https://hub.docker.com/r/nodered/node-red/) up to Node-RED version 1.1.3 contained the following line: 10 | 11 | ``` 12 | VOLUME [/data] 13 | ```` 14 | 15 | This single line is now removed in recently published docker images (starting from Node-RED version 1.2.0). 16 | 17 | ### Impact of the line "`VOLUME [/data]`" 18 | 19 | See also [documentation for `VOLUME` instruction in dockerfile reference](https://docs.docker.com/engine/reference/builder/#volume) 20 | 21 | When a docker container is created without explicitly specifying a docker volume for `/data` folder then this line assures that a docker volume is automatically created and that the contents of the `/data` folder from the base image are copied to this automatically created volume. 22 | 23 | Note that removing the container, will not automatically remove this volume ! So you need to explicitly remove this volume if you no longer need it - which is not that easy as this anonymous volume has a randomly generated name like "`76cc15fa0b6184c7ce6a2a4b865cdf2cd7d62661256e8ba2b5a4dff2d30da1e4`". 24 | 25 | #### Unexpected behaviour when used with `docker-compose` 26 | 27 | This section explains unexpected behaviour when using docker images containing also the flows based on old Node-RED docker images (v1.1.3 and earlier) in combination with docker-compose. 28 | 29 | The problem scenario: 30 | 31 | 1. Someone creates a docker image `Img:1.0.0` which contains the Node-RED flow `Flow_1.0.0` 32 | 2. You have created a docker-compose file defining a service using this `Img:1.0.0` 33 | 3. The command "`docker-compose up`" will properly start this service and the very first time will create an anonymous volume that is mounted to the `/data` folder where the flow `Flow_1.0.0` is copied to. 34 | 4. Someone updates the docker image (= `Img:2.0.0`) which now contains the updated Node-RED flows `Flow_2.0.0`. 35 | 5. You want to use the new version of the image, so You run first the `docker-compose pull` command. Which will correctly download image `Img:2.0.0` 36 | 6. then you run command "`docker-compose up`" to deploy the new version but this will indeed deploy the new version of the image but it won't create a new volume: it will still use the same volume created in step 3. This also means that it will NOT copy flow `FLow_2.0.0` from the image to the mounted volume and consequently it will still use the old flow `Flow_1.0.0` instead of the new flow `Flow_2.0.0` 37 | 38 | So in order to avoid this problem you must explicitly remove the "anonymous volume" before executing step 6. 39 | 40 | FYI it is especially the "`(preserving mounted volumes)`" in the following paragraph of [docker-compose up documentation](https://docs.docker.com/compose/reference/up/) that explains why we have this problem with docker-compose. 41 | 42 | ``` 43 | If there are existing containers for a service, and the service’s configuration or image was changed after the container’s creation, docker-compose up picks up the changes by stopping and recreating the containers (preserving mounted volumes). 44 | ``` 45 | 46 | # Should I care about this change ? 47 | 48 | ### A. I have explicitly specified a volume to be mounted to /data. 49 | 50 | In case you have explicitly defined a volume to be mounted to the "`/data`" folder when creating the container then nothing changes for you. Everything will work exactly as before. When upgrading to a new docker image without the "`VOLUME [/data]`" it will still use the latest flows you have saved just before the upgrade. 51 | 52 | ### B. I didn't explicitly specify a volume to be mounted to /data. 53 | 54 | It still means that the file changes saved in the "`/data`" folder when using new Node-RED docker image (= image without the "`VOLUME [/data]`") are not lost when stopping and starting the container or rebooting the docker host machine ! They are persisted in the container writeable layer and will be kept as long as the container is not removed. 55 | 56 | So when using the standard docker commands to start/stop/run/remove/create containers everything will work as before. Moreover with new Node-RED docker images you no longer need to cleanup those anonymous volumes when removing the container. 57 | 58 | #### **ATTENTION**: I am using `docker-compose` and have not explicitly specified a volume to be mounted to /data 59 | 60 | So when upgrading a service to use a new Node-RED docker image (= image without the "`VOLUME [/data]`") using the "`docker-compose up`" command you will notice that the anonymous volume is still mounted to the `/data` folder as the "`docker-compose up`" command is preserving mounted volumes. This might be a good thing in case you still want to use the flows you have saved before the upgrade. 61 | ... but this might also be an undesired thing, in case you want to use the flows in the "`/data`" folders of the new docker image. The fix for this is easy. You first have to remove the respective docker container and only then run the "`docker-compose up`" command as this command will now rebuild the container without mounting any volume. 62 | 63 | When upgrading from one image to another image and both images are based on a recent Node-RED docker image (= image without the "`VOLUME [/data]`") AND there was no preserved mount of the `/data` folder then the `/data` folder is completely replaced by the `/data` folder of the new image ! 64 | 65 | ## Support 66 | 67 | If you are still stuck or need more support then we recommend to open a topic in the [Node-RED forum](https://discourse.nodered.org/) for this. --------------------------------------------------------------------------------