Quick Start Guide
Get your IoT greenhouse monitoring system up and running in less than 5 minutes using Docker Compose.
Prerequisites
Before you begin, ensure you have the following installed:
Docker Compose Docker Compose v2.0+ (Included with Docker Desktop)
Java 21 is optional - Only needed for local development without Docker. The Docker image includes the JRE.
Step 1: Clone the Repository
Clone from GitHub
git clone https://github.com/apptolast/InvernaderosAPI.git
cd InvernaderosAPI
The system requires secure passwords for database and MQTT connections.
Copy the example environment file
Generate secure passwords
Use OpenSSL to generate strong passwords: # Generate 5 random passwords
openssl rand -base64 32
openssl rand -base64 32
openssl rand -base64 32
openssl rand -base64 32
openssl rand -base64 32
Edit the .env file
Open .env in your text editor and replace placeholders: # TimescaleDB (Time-series Database)
TIMESCALE_DB_NAME = greenhouse_timeseries
TIMESCALE_USER = admin
TIMESCALE_PASSWORD =< paste_generated_password_1 >
# PostgreSQL Metadata Database
METADATA_DB_NAME = greenhouse_metadata
METADATA_USER = admin
METADATA_PASSWORD =< paste_generated_password_2 >
# Redis Cache
REDIS_HOST = redis
REDIS_PORT = 6379
REDIS_PASSWORD =< paste_generated_password_3 >
# MQTT Broker (EMQX)
MQTT_BROKER_URL = tcp://emqx:1883
MQTT_USERNAME = mqtt_user
MQTT_PASSWORD =< paste_generated_password_4 >
MQTT_CLIENT_ID_PREFIX = api_local_001
# EMQX Dashboard
EMQX_DASHBOARD_USERNAME = admin
EMQX_DASHBOARD_PASSWORD =< paste_generated_password_5 >
# Spring Boot
SPRING_PROFILES_ACTIVE = local
JAVA_OPTS = -Xms256m -Xmx512m
Never use default passwords in production! Generate unique, strong passwords for each service and rotate them regularly.
Copy application configuration
cp application-local.yaml.example application-local.yaml
This file configures the Spring Boot application. The default settings are optimized for local development.
The application-local.yaml file is volume-mounted into the Docker container at /app/config/application.yaml. You can modify settings without rebuilding the image.
Step 4: Start All Services
Start with Docker Compose
This command will:
Pull required Docker images (TimescaleDB, PostgreSQL, Redis, EMQX)
Build the Spring Boot API image
Start all 5 services in detached mode
Create Docker networks and volumes
Monitor startup logs
docker-compose logs -f api
Wait for this message: Started InvernaderosApplication in X.XXX seconds
Press Ctrl+C to stop following logs.
Verify all services are running
Expected output: NAME STATUS
invernaderos-api Up (healthy)
invernaderos-emqx Up (healthy)
invernaderos-postgres Up (healthy)
invernaderos-redis Up (healthy)
invernaderos-timescaledb Up (healthy)
Step 5: Verify API is Running
Health check endpoint
curl http://localhost:8080/actuator/health
Expected response:
Check cache status
curl http://localhost:8080/api/v1/greenhouse/cache/info
Expected response: {
"totalMessages" : 0 ,
"ttlSeconds" : 86400 ,
"maxCapacity" : 1000 ,
"utilizationPercentage" : 0.0 ,
"cacheType" : "Redis Sorted Set"
}
Step 6: Explore the API
Open your browser and access the interactive API documentation:
Swagger UI Interactive API explorer with try-it-out functionality
OpenAPI Spec OpenAPI 3.0 JSON specification
EMQX Dashboard MQTT broker management (login with EMQX credentials)
Actuator Health, metrics, and monitoring endpoints
Step 7: Test with Sample Data
Now let’s send a test MQTT message to verify the system is working.
Install mosquitto-clients: # macOS
brew install mosquitto
# Ubuntu/Debian
sudo apt-get install mosquitto-clients
# Windows (use WSL or download from mosquitto.org)
Publish a test message: mosquitto_pub -h localhost -p 1883 \
-u mqtt_user -P < your_mqtt_passwor d > \
-t "GREENHOUSE/TEST" \
-m '{
"TEMPERATURA INVERNADERO 01": 24.5,
"HUMEDAD INVERNADERO 01": 65.3,
"TEMPERATURA INVERNADERO 02": 23.8,
"HUMEDAD INVERNADERO 02": 68.2,
"TEMPERATURA INVERNADERO 03": 25.1,
"HUMEDAD INVERNADERO 03": 64.7,
"INVERNADERO_01_SECTOR_01": 1.0,
"INVERNADERO_01_SECTOR_02": 0.0,
"INVERNADERO_01_SECTOR_03": 1.0,
"INVERNADERO_01_SECTOR_04": 0.0,
"INVERNADERO_02_SECTOR_01": 1.0,
"INVERNADERO_02_SECTOR_02": 1.0,
"INVERNADERO_02_SECTOR_03": 0.0,
"INVERNADERO_02_SECTOR_04": 1.0,
"INVERNADERO_03_SECTOR_01": 0.0,
"INVERNADERO_03_SECTOR_02": 1.0,
"INVERNADERO_03_SECTOR_03": 1.0,
"INVERNADERO_03_SECTOR_04": 0.0,
"INVERNADERO_01_EXTRACTOR": 850.5,
"INVERNADERO_02_EXTRACTOR": 920.3,
"INVERNADERO_03_EXTRACTOR": 780.2,
"RESERVA": 0.0
}'
Install paho-mqtt: Create test_mqtt.py: import paho.mqtt.client as mqtt
import json
# MQTT Configuration
broker = "localhost"
port = 1883
username = "mqtt_user"
password = "your_mqtt_password" # From .env
topic = "GREENHOUSE/TEST"
# Sample sensor data
payload = {
"TEMPERATURA INVERNADERO 01" : 24.5 ,
"HUMEDAD INVERNADERO 01" : 65.3 ,
"TEMPERATURA INVERNADERO 02" : 23.8 ,
"HUMEDAD INVERNADERO 02" : 68.2 ,
"TEMPERATURA INVERNADERO 03" : 25.1 ,
"HUMEDAD INVERNADERO 03" : 64.7 ,
"INVERNADERO_01_SECTOR_01" : 1.0 ,
"INVERNADERO_01_SECTOR_02" : 0.0 ,
"INVERNADERO_01_SECTOR_03" : 1.0 ,
"INVERNADERO_01_SECTOR_04" : 0.0 ,
"INVERNADERO_02_SECTOR_01" : 1.0 ,
"INVERNADERO_02_SECTOR_02" : 1.0 ,
"INVERNADERO_02_SECTOR_03" : 0.0 ,
"INVERNADERO_02_SECTOR_04" : 1.0 ,
"INVERNADERO_03_SECTOR_01" : 0.0 ,
"INVERNADERO_03_SECTOR_02" : 1.0 ,
"INVERNADERO_03_SECTOR_03" : 1.0 ,
"INVERNADERO_03_SECTOR_04" : 0.0 ,
"INVERNADERO_01_EXTRACTOR" : 850.5 ,
"INVERNADERO_02_EXTRACTOR" : 920.3 ,
"INVERNADERO_03_EXTRACTOR" : 780.2 ,
"RESERVA" : 0.0
}
# Connect and publish
client = mqtt.Client()
client.username_pw_set(username, password)
client.connect(broker, port, 60 )
client.publish(topic, json.dumps(payload))
client.disconnect()
print ( "✅ Message published successfully!" )
Run the script: Query recent messages via REST: curl http://localhost:8080/api/v1/greenhouse/messages/recent?tenantId=TEST & limit = 10
Expected response: [
{
"timestamp" : "2025-03-03T21:30:00Z" ,
"TEMPERATURA INVERNADERO 01" : 24.5 ,
"HUMEDAD INVERNADERO 01" : 65.3 ,
"TEMPERATURA INVERNADERO 02" : 23.8 ,
"HUMEDAD INVERNADERO 02" : 68.2 ,
// ... all 22 fields
"greenhouseId" : null ,
"tenantId" : "TEST"
}
]
The API automatically processes MQTT messages, caches them in Redis, saves to TimescaleDB, and broadcasts via WebSocket to connected clients.
Docker Compose Services
The docker-compose.yaml defines 5 services:
Service Container Name Ports Purpose api invernaderos-api8080 Spring Boot API timescaledb invernaderos-timescaledb5432 Time-series database postgresql-metadata invernaderos-postgres-metadata5433→5432 Metadata database redis invernaderos-redis6379 Cache emqx invernaderos-emqx1883, 8083, 18083 MQTT broker
View complete docker-compose.yaml
version : '3.8'
services :
# TimescaleDB - Time-series Database
timescaledb :
image : timescale/timescaledb:latest-pg16
container_name : invernaderos-timescaledb
environment :
POSTGRES_DB : ${TIMESCALE_DB_NAME:-greenhouse_timeseries}
POSTGRES_USER : ${TIMESCALE_USER:-admin}
POSTGRES_PASSWORD : ${TIMESCALE_PASSWORD}
ports :
- "5432:5432"
volumes :
- timescaledb_data:/var/lib/postgresql/data
healthcheck :
test : [ "CMD-SHELL" , "pg_isready -U ${TIMESCALE_USER}" ]
interval : 10s
timeout : 5s
retries : 5
# PostgreSQL - Metadata Database
postgresql-metadata :
image : postgres:16-alpine
container_name : invernaderos-postgres-metadata
environment :
POSTGRES_DB : ${METADATA_DB_NAME:-greenhouse_metadata}
POSTGRES_USER : ${METADATA_USER:-admin}
POSTGRES_PASSWORD : ${METADATA_PASSWORD}
ports :
- "5433:5432"
volumes :
- postgres_metadata_data:/var/lib/postgresql/data
healthcheck :
test : [ "CMD-SHELL" , "pg_isready -U ${METADATA_USER}" ]
interval : 10s
timeout : 5s
retries : 5
# Redis - Cache
redis :
image : redis:7-alpine
container_name : invernaderos-redis
command : redis-server --requirepass ${REDIS_PASSWORD}
ports :
- "6379:6379"
volumes :
- redis_data:/data
healthcheck :
test : [ "CMD" , "redis-cli" , "ping" ]
interval : 10s
timeout : 3s
retries : 5
# EMQX - MQTT Broker
emqx :
image : emqx/emqx:latest
container_name : invernaderos-emqx
environment :
EMQX_DASHBOARD__DEFAULT_USERNAME : ${EMQX_DASHBOARD_USERNAME:-admin}
EMQX_DASHBOARD__DEFAULT_PASSWORD : ${EMQX_DASHBOARD_PASSWORD}
ports :
- "1883:1883" # MQTT
- "8083:8083" # MQTT/WebSocket
- "18083:18083" # Dashboard
healthcheck :
test : [ "CMD" , "/opt/emqx/bin/emqx" , "ping" ]
interval : 10s
timeout : 5s
retries : 5
# Invernaderos API - Spring Boot Application
api :
build :
context : .
dockerfile : Dockerfile
container_name : invernaderos-api
environment :
SPRING_PROFILES_ACTIVE : ${SPRING_PROFILES_ACTIVE:-local}
TIMESCALE_PASSWORD : ${TIMESCALE_PASSWORD}
METADATA_PASSWORD : ${METADATA_PASSWORD}
REDIS_HOST : redis
REDIS_PORT : 6379
REDIS_PASSWORD : ${REDIS_PASSWORD}
MQTT_BROKER_URL : ${MQTT_BROKER_URL:-tcp://emqx:1883}
MQTT_USERNAME : ${MQTT_USERNAME}
MQTT_PASSWORD : ${MQTT_PASSWORD}
MQTT_CLIENT_ID_PREFIX : ${MQTT_CLIENT_ID_PREFIX:-api_local_001}
ports :
- "8080:8080"
volumes :
- ./application-local.yaml:/app/config/application.yaml:ro
depends_on :
timescaledb :
condition : service_healthy
postgresql-metadata :
condition : service_healthy
redis :
condition : service_healthy
emqx :
condition : service_healthy
healthcheck :
test : [ "CMD" , "curl" , "-f" , "http://localhost:8080/actuator/health" ]
interval : 30s
timeout : 3s
retries : 3
start_period : 60s
volumes :
timescaledb_data :
postgres_metadata_data :
redis_data :
Common Commands
View Logs
Service Management
Database Access
# Follow API logs
docker-compose logs -f api
# View all logs
docker-compose logs
# Last 100 lines
docker-compose logs --tail=100 api
Troubleshooting
Check if ports are already in use: lsof -i :8080 # API
lsof -i :5432 # TimescaleDB
lsof -i :5433 # PostgreSQL
lsof -i :6379 # Redis
lsof -i :1883 # MQTT
Stop conflicting services or modify ports in docker-compose.yaml
Database connection errors
Check environment variables: Verify passwords match in .env and application-local.yaml Reset databases (⚠️ deletes all data): docker-compose down -v
docker-compose up -d
MQTT messages not received
Check MQTT credentials in .env: Test MQTT connection: mosquitto_pub -h localhost -p 1883 \
-u mqtt_user -P < passwor d > \
-t "test" -m "hello"
View EMQX logs:
View API logs: docker-compose logs api --tail=100
Check health status: curl http://localhost:8080/actuator/health
Verify all services are healthy:
Next Steps
Architecture Understand the system design and components
API Reference Explore all REST endpoints
WebSocket Connect to real-time data stream
Congratulations! Your Invernaderos API is now running. You can start sending sensor data via MQTT and querying it via REST or WebSocket.