Step-by-step guide to deploy Agrogat as a field edge gateway on Raspberry Pi 4 or 5 running Ubuntu 24.04 LTS Server (64-bit).
> Software-only reference (.env, migrations, sync): see setup.md.
What you will build
| Component | Role |
|---|---|
| Raspberry Pi 4/5 | Edge compute node (local ingest + dashboard) |
| Ubuntu 24.04 Server | OS (headless, no desktop) |
| Apache + PHP 8.3 | Web server and application runtime |
| PostgreSQL 16 | Local time-series and config store |
| Agrogat | IoT gateway application (this repository) |
Typical use: LoRaWAN or HTTP sensors → local ingest.php → PostgreSQL → optional sync to a central server.
Step 1 — Hardware and SD card
Minimum recommended
- Raspberry Pi 4 (4 GB+) or 5 (4 GB+)
- MicroSD 32 GB+ (Class 10 / A2) or USB SSD (preferred for production)
- Stable 5 V power supply (official Pi PSU recommended)
- Ethernet cable or reliable Wi-Fi (field sites: prefer Ethernet)
- Optional: LoRaWAN concentrator / packet forwarder on the same LAN or USB
Flash Ubuntu Server 24.04 for Raspberry Pi
- Download Ubuntu Server 24.04 LTS (64-bit) for Raspberry Pi from Ubuntu Raspberry Pi images.
- Flash with Raspberry Pi Imager or
ddto the SD card / SSD. - In Imager OS customisation (recommended):
- Set hostname, e.g.
agrogat-edge-01 - Create user, e.g.
agrogat - Enable SSH with password or public key
- Configure Wi-Fi if not using Ethernet
- Insert media, power on the Pi, wait ~2 minutes for first boot.
Step 2 — First login and system update
From your workstation:
ssh agrogat@<PI_IP_ADDRESS>Replace <PI_IP_ADDRESS> with the address from your router or raspberrypi.local if mDNS works.
On the Pi:
# System packages up to date
sudo apt update && sudo apt upgrade -y
# Useful tools
sudo apt install -y git curl unzip software-properties-common
# Set timezone (example: Norway)
sudo timedatectl set-timezone Europe/Oslo
# Optional: set a descriptive hostname
sudo hostnamectl set-hostname agrogat-edge-01Reboot if the kernel was updated:
sudo rebootStep 3 — Install Apache, PHP, and PostgreSQL
Ubuntu 24.04 provides PHP 8.3 — compatible with Agrogat (requires PHP 8.1+).
sudo apt install -y \
apache2 \
libapache2-mod-php \
php-cli \
php-pgsql \
php-json \
php-mbstring \
php-xml \
postgresql \
postgresql-contribEnable Apache modules:
sudo a2enmod rewrite headers
sudo systemctl enable --now apache2 postgresqlVerify versions:
php -v
apache2 -v
sudo -u postgres psql -c "SELECT version();"Step 4 — Create PostgreSQL database and user
Choose a database name (example: agrogat_iot). Use a strong password.
sudo -u postgres psql -v ON_ERROR_STOP=1 <<'SQL'
CREATE ROLE trat_admin WITH LOGIN PASSWORD 'CHANGE_ME_STRONG_PASSWORD';
CREATE DATABASE agrogat_iot OWNER trat_admin;
GRANT ALL PRIVILEGES ON DATABASE agrogat_iot TO trat_admin;
SQLTest connection as the app user:
psql -h localhost -U trat_admin -d agrogat_iot -c "SELECT 1;"Step 5 — Clone the Agrogat application
Install under /var/www/html/agrogat (matches project conventions and Apache examples).
sudo mkdir -p /var/www/html
sudo chown $USER:www-data /var/www/html
cd /var/www/html
git clone git@github.com:monsegaard/agrogat.git agrogat
cd agrogatPrivate repository: add your SSH deploy key to GitHub first, or clone via HTTPS with a Personal Access Token.
If the directory already exists, use git pull instead.
Step 6 — Configure Apache
Option A — Access by IP or local hostname (typical edge node)
Create a site config:
sudo tee /etc/apache2/sites-available/agrogat-edge.conf <<'EOF'
<VirtualHost *:80>
ServerName agrogat-edge.local
DocumentRoot /var/www/html/agrogat
ErrorLog ${APACHE_LOG_DIR}/agrogat-error.log
CustomLog ${APACHE_LOG_DIR}/agrogat-access.log combined
<Directory /var/www/html/agrogat>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
EOF
sudo a2dissite 000-default.conf
sudo a2ensite agrogat-edge.conf
sudo apache2ctl configtest && sudo systemctl reload apache2Open http://<PI_IP_ADDRESS>/ in a browser — you should see the Agrogat welcome page.
Option B — Public domain with TLS
If the Pi has a public DNS name, copy the project template and run Certbot:
sudo cp /var/www/html/agrogat/deploy/apache-agrogat.com.conf /etc/apache2/sites-available/agrogat.conf
# Edit ServerName if needed
sudo a2ensite agrogat.conf
sudo apt install -y certbot python3-certbot-apache
sudo certbot --apache -d your-domain.example.comStep 7 — Create .env (application configuration)
cd /var/www/html/agrogat
cp .env.example .env
nano .envMinimum values for an edge gateway:
DB_HOST=localhost
DB_PORT=5432
DB_NAME=agrogat_iot
DB_USER=trat_admin
DB_PASS=CHANGE_ME_STRONG_PASSWORD
INGEST_API_KEY=GENERATE_WITH_COMMAND_BELOW
GATEWAY_CODE=TRAT-GATEWAY-001
GATEWAY_CLUSTER=TRAT
# Mark this node as an edge gateway (welcome page badge)
IS_EDGE_GATEWAY=1
# Optional: push readings to central server
# CENTRAL_SYNC_URL=https://central.example.com/api/sync_receive.php
# SYNC_API_KEY=same_secret_on_central_and_edgeGenerate API keys:
php -r "echo bin2hex(random_bytes(32)) . PHP_EOL;"Run twice if you need separate values for INGEST_API_KEY and SYNC_API_KEY.
Secure the file:
chmod 640 .env
sudo chown $USER:www-data .envStep 8 — File permissions for data/
The simulator and LIVE mode store settings in data/settings.json:
cd /var/www/html/agrogat
mkdir -p data
sudo chown -R www-data:www-data data
chmod 775 dataEnsure Apache can read the rest of the tree:
sudo chown -R $USER:www-data /var/www/html/agrogat
find /var/www/html/agrogat -type d -exec chmod 775 {} \;
find /var/www/html/agrogat -type f -exec chmod 664 {} \;Step 9 — Run database migrations
Use the bundled script (runs 001 → 001b → 002 … 005 in order):
cd /var/www/html/agrogat
sudo ./scripts/migrate_postgres.sh agrogat_iotExpected output ends with: Migrasjoner fullført for database «agrogat_iot».
Manual alternative — see setup.md.
Step 10 — Create the first administrator
cd /var/www/html/agrogat
php scripts/create_admin.php admin@example.com 'YourStrongPassword'Sign in at http://<PI_IP_ADDRESS>/login.php.
Step 11 — Register this gateway in the database
After login, open the dashboard → Gateways tab and confirm a row exists with code matching GATEWAY_CODE in .env (default seed: TRAT-GATEWAY-001).
For a dedicated edge node, create a new gateway code, e.g. FIELD-GW-01, and update .env:
GATEWAY_CODE=FIELD-GW-01
GATEWAY_CLUSTER=TRATNew ingest readings will then link to the correct gateway.
Step 12 — Optional: firewall (UFW)
If the Pi is exposed on a network, allow SSH and HTTP only:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
# If using HTTPS:
# sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw statusDo not expose PostgreSQL (5432) to the internet.
Step 13 — Optional: cron sync to central server
When CENTRAL_SYNC_URL and SYNC_API_KEY are configured, add cron jobs:
sudo crontab -u www-data -eAdd:
*/5 * * * * /usr/bin/php /var/www/html/agrogat/scripts/sync_push_to_central.php >> /var/log/agrogat-sync.log 2>&1
*/15 * * * * /usr/bin/php /var/www/html/agrogat/scripts/sync_push_entities_to_central.php >> /var/log/agrogat-sync-entities.log 2>&1Test manually:
sudo -u www-data php /var/www/html/agrogat/scripts/sync_push_to_central.phpDetails: setup.md.
Step 14 — Verify the installation
Welcome page (guest):
curl -s -o /dev/null -w "%{http_code}\n" http://localhost/Expect 200.
Ingest API:
cd /var/www/html/agrogat
set -a && source .env && set +a
curl -s -X POST http://localhost/ingest.php \
-H "Content-Type: application/json" \
-H "X-API-Key: ${INGEST_API_KEY}" \
-d '{"sensor_id":"test-001","payload":{"vwc":42.5,"temperature_c":12.3}}'Expected:
{"status":"success","id":"1"}Dashboard: log in, check tabs (Clients, Sensors, Gateways, Data). Set Simulator or LIVE under Operations if needed.
API docs: http://<PI_IP_ADDRESS>/docs/api.php
Step 15 — Connect field sensors
- Point LoRaWAN network servers or sensor gateways to
http://<PI_IP_ADDRESS>/ingest.phpwith headerX-API-Key. - Register each
sensor_idunder Sensors in the dashboard (or via seed scripts). - For Dragino LSE01 soil probes, see sensors-dragino-lse01.md.
- For RS485 / piezometer hex payloads, see api.md.
Troubleshooting
| Problem | Solution |
|---|---|
403 Forbidden on web |
Check Apache DocumentRoot and directory permissions |
500 or blank page |
sudo tail -f /var/log/apache2/agrogat-error.log |
| Database connection failed | Verify .env credentials; sudo systemctl status postgresql |
401 on ingest |
Wrong or missing X-API-Key; must match INGEST_API_KEY |
| Migration fails on role | Create trat_admin first (Step 4) |
sensor_data missing before 002 |
Run 001b_sensor_data.sql or use migrate_postgres.sh |
| Git clone fails (private repo) | Add SSH key to GitHub or use HTTPS + token |
| Slow SD card | Move to USB SSD; enable log rotation for sync logs |
Maintenance
Update application code:
cd /var/www/html/agrogat
git pull
# Run any new migrations:
sudo ./scripts/migrate_postgres.sh agrogat_iot
sudo systemctl reload apache2Backup database:
sudo -u postgres pg_dump agrogat_iot > agrogat_iot_$(date +%F).sqlRelated documentation
| Document | Contents |
|---|---|
| setup.md | .env reference, sync, migrations detail |
| api.md | Ingest and REST endpoints |
| examples.md | Sample curl and client code |
| sensors-dragino-lse01.md | Dragino LSE01 integration |