Skip to Content

← All functionalities

Automated agricultural irrigationRaspberry Pi (Docker)HTTPMySQLInfrastructure

A four-container automation stack on one Raspberry Pi

One Raspberry Pi can host a complete supervision platform if each piece lives in its own container. This Docker Compose stack runs four: Node-RED for plant logic, MySQL for the alarm history, a Laravel frontend with multi-company login and Nginx as the only container exposed to the network. Named volumes survive recreation and secrets stay in a .env file. It is the exact infrastructure of a real automated irrigation deployment.

One door in — Nginx

Only Nginx publishes a port; Laravel, Node-RED and MySQL sit on an internal bridge network. The proxy routes / to the PHP frontend and /nodered/ to the editor and dashboard, so the farm router needs a single forwarded port and the database is simply unreachable from outside. For local debugging you uncomment one line to expose 1880 directly.

Ordered startup with healthchecks

Laravel and Node-RED declare depends_on with service_healthy against MySQL, whose healthcheck pings mysqladmin every 10 seconds. On a Raspberry Pi cold boot — where MySQL may take half a minute on an SD card — this prevents the classic storm of connection-refused errors and crashed migrations on power restore after an outage in the field.

Secrets and state outside the images

All credentials (MYSQL_PASSWORD, OPENWEATHER_API_KEY, TELEGRAM_TOKEN) come from a .env file next to the compose file and are injected as environment variables, so flows and images stay shareable. Named volumes persist MySQL data and the Node-RED palette, and the alarm schema mounts into docker-entrypoint-initdb.d so the first boot creates the tables by itself.

A snippet from the implementation

Straight from the example as deployed on the Raspberry Pi (Docker) — copy it freely:

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

The full example is a complete program — wiring header, setup and main loop — ready to adapt to your application.

Frequently asked questions

Is a Raspberry Pi powerful enough for four containers?

Comfortably, on a Pi 4 with 4 GB. Node-RED and MySQL are the only meaningful consumers; Nginx and PHP-FPM idle in the low tens of megabytes. Use a 64-bit OS and a quality SD card or SSD for the MySQL volume.

How do I update one service without touching the rest?

docker compose pull nodered followed by docker compose up -d nodered recreates only that container. Flows, credentials and installed palette nodes live in the nodered_data volume, so they survive the upgrade.

Why set the timezone in the Node-RED container?

The irrigation schedulers fire at local civil time, morning and evening. Without TZ=Europe/Madrid the container runs in UTC and the cycles drift one or two hours depending on daylight saving — irrigating at the wrong time of day.

Related functionalities