Skip to Content

← Raspberry Pi Docker Stack: Node-RED, MySQL, Nginx, Laravel

Automated agricultural irrigationRaspberry Pi (Docker)HTTPMySQLInfrastructure

Raspberry Pi Docker Stack: Node-RED, MySQL, Nginx, Laravel — full example

Run a four-container industrial stack on a Raspberry Pi with Docker Compose: Node-RED logic, MySQL history, Laravel frontend and Nginx reverse proxy.

Complete, runnable program for the Raspberry Pi (Docker) (docker-compose-irrigation.yml): wiring header, requirements and integration notes included.

Download the full project pack — freeThis example + the related ones + bill of materials

Read-only preview.

# =============================================================================
# COMPLETE EXAMPLE — Multi-container Docker stack for agricultural irrigation
#
# Hardware:  Raspberry Pi (Docker + docker compose)
# Based on:  automated agricultural irrigation project
#
# Requirements:
#   - Raspberry Pi OS 64-bit with Docker and the compose plugin installed.
#   - A .env file next to this compose file with the credentials:
#       MYSQL_PASSWORD=********
#       MYSQL_ROOT_PASSWORD=********
#       OPENWEATHER_API_KEY=********
#       TELEGRAM_TOKEN=********
#       TELEGRAM_CHAT_ID=********
#
# Architecture (4 containers on an internal network):
#   nginx    :80   reverse proxy — routes / to the Laravel frontend and
#                  /nodered/ to the Node-RED editor and dashboard
#   laravel        PHP-FPM frontend with multi-company login
#   nodered  :1880 irrigation logic, Modbus, Telegram, OpenWeather
#   mysql    :3306 alarm history and configuration (internal network only)
# =============================================================================

services:

  # ---- Reverse proxy: the only container exposed to the outside --------------
  nginx:
    image: nginx:stable-alpine
    container_name: irrigation-nginx
    restart: unless-stopped
    ports:
      - "80:80"                       # only port published on the Raspberry Pi
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
      - laravel_public:/var/www/html/public:ro
    depends_on:
      - laravel
      - nodered
    networks: [irrigation]

  # ---- Laravel frontend (multi-company login, alarm history) -----------------
  laravel:
    build: ./laravel                  # Dockerfile with php-fpm + composer
    container_name: irrigation-laravel
    restart: unless-stopped
    environment:
      APP_ENV: production
      DB_HOST: mysql
      DB_DATABASE: irrigation
      DB_USERNAME: irrigation
      DB_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - laravel_public:/var/www/html/public
    depends_on:
      mysql:
        condition: service_healthy    # do not start until MySQL responds
    networks: [irrigation]

  # ---- Node-RED: all the plant logic ------------------------------------------
  nodered:
    image: nodered/node-red:latest
    container_name: irrigation-nodered
    restart: unless-stopped
    environment:
      TZ: Europe/Madrid               # the irrigation schedulers depend on the TZ
      OPENWEATHER_API_KEY: ${OPENWEATHER_API_KEY}
      TELEGRAM_TOKEN: ${TELEGRAM_TOKEN}
      TELEGRAM_CHAT_ID: ${TELEGRAM_CHAT_ID}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - nodered_data:/data            # flows, credentials and palette persist
    # No "ports": accessed via nginx (/nodered/). To debug locally,
    # uncomment the following line:
    #   - "1880:1880"
    depends_on:
      mysql:
        condition: service_healthy
    networks: [irrigation]

  # ---- MySQL: alarm history and configuration ---------------------------------
  mysql:
    image: mysql:8.0
    container_name: irrigation-mysql
    restart: unless-stopped
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: irrigation
      MYSQL_USER: irrigation
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - mysql_data:/var/lib/mysql
      # The schema (alarms, alarms_describe) is created only on first startup:
      - ./mysql/alarm-history-mysql.sql:/docker-entrypoint-initdb.d/01-schema.sql:ro
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-p${MYSQL_ROOT_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 10
    networks: [irrigation]

# ---- Named volumes: they survive container re-creations ----------------------
volumes:
  mysql_data:
  nodered_data:
  laravel_public:

# ---- Internal network: MySQL and Laravel are not reachable from outside ------
networks:
  irrigation:
    driver: bridge
Download the full project pack — freeThis example + the related ones + bill of materials