Service Deployment
This guide covers deploying aranet-service as a persistent background service on various platforms.
Deployment Options
Section titled “Deployment Options”| Platform | Service Manager | Config Location |
|---|---|---|
| macOS | launchd | ~/Library/LaunchAgents/ |
| Linux | systemd | /etc/systemd/system/ |
| Docker | docker-compose | Container orchestration |
| Windows | NSSM | Windows Service |
macOS (launchd)
Section titled “macOS (launchd)”User-Level Service
Section titled “User-Level Service”-
Install aranet-service
Terminal window cargo install aranet-service --features full -
Install as user service
Terminal window aranet-service service install --user -
Start the service
Terminal window aranet-service service start --user -
Check status
Terminal window aranet-service service status --user
Manual Configuration
Section titled “Manual Configuration”Create ~/Library/LaunchAgents/dev.rye.aranet.plist:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict> <key>Label</key> <string>dev.rye.aranet</string> <key>ProgramArguments</key> <array> <string>/usr/local/bin/aranet-service</string> <string>run</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>StandardOutPath</key> <string>/usr/local/var/log/aranet.log</string> <key>StandardErrorPath</key> <string>/usr/local/var/log/aranet.err</string> <key>WorkingDirectory</key> <string>/usr/local/var/aranet</string></dict></plist>Load the service:
launchctl load ~/Library/LaunchAgents/dev.rye.aranet.plistLinux (systemd)
Section titled “Linux (systemd)”System-Level Service
Section titled “System-Level Service”-
Build and install
Terminal window cargo build --release -p aranet-service --features fullsudo cp target/release/aranet-service /usr/local/bin/ -
Create service user
Terminal window sudo useradd -r -s /bin/false aranetsudo mkdir -p /var/lib/aranetsudo chown aranet:aranet /var/lib/aranet -
Install the service file
Terminal window aranet-service service install# Or manually: sudo cp distribution/systemd/aranet.service /etc/systemd/system/ -
Enable and start
Terminal window sudo systemctl daemon-reloadsudo systemctl enable --now dev.rye.aranet
Service File
Section titled “Service File”Create /etc/systemd/system/aranet.service:
[Unit]Description=Aranet Sensor Collector ServiceDocumentation=https://github.com/cameronrye/aranetAfter=network-online.target bluetooth.targetWants=network-online.target
[Service]Type=simpleUser=aranetGroup=aranetExecStart=/usr/local/bin/aranet-service runRestart=on-failureRestartSec=5StartLimitIntervalSec=60StartLimitBurst=3
# PathsWorkingDirectory=/var/lib/aranetReadWritePaths=/var/lib/aranet
# Security hardeningProtectSystem=strictProtectHome=truePrivateTmp=trueNoNewPrivileges=true
# Bluetooth accessAmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAWCapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW
[Install]WantedBy=multi-user.targetUser-Level Service
Section titled “User-Level Service”# Create user service directorymkdir -p ~/.config/systemd/user
# Copy service filecp distribution/systemd/aranet.service ~/.config/systemd/user/
# Edit to remove User/Group and adjust paths# Then enablesystemctl --user daemon-reloadsystemctl --user enable --now dev.rye.aranetDocker
Section titled “Docker”A multi-stage Dockerfile and docker-compose.yml are provided at the repository root. BLE access requires privileged mode and D-Bus passthrough.
Quick Start
Section titled “Quick Start”# Standalone service with BLEdocker compose up -d
Full Monitoring Stack
Section titled “Full Monitoring Stack”Use the docker/ directory for a Prometheus + Grafana stack:
docker compose -f docker/docker-compose.yml up -dBuild from Source
Section titled “Build from Source”docker build -t aranet-service .docker run -d --name aranet-service \ --privileged --network host \ -v /var/run/dbus:/var/run/dbus \ -v aranet-data:/data \ -v ./server.toml:/app/server.toml:ro \ -e RUST_LOG=aranet_service=info \ aranet-serviceProduction Deployment
Section titled “Production Deployment”services: aranet-service: build: . restart: always ports: - "8080:8080" volumes: - aranet-data:/data - ./server.toml:/app/server.toml:ro - /var/run/dbus:/var/run/dbus privileged: true network_mode: host environment: - RUST_LOG=aranet_service=info healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"] interval: 30s timeout: 10s retries: 3 start_period: 5s
volumes: aranet-data:Configuration
Section titled “Configuration”Server Configuration
Section titled “Server Configuration”Create ~/.config/aranet/server.toml:
[server]bind = "127.0.0.1:8080"
[storage]path = "~/.local/share/aranet/data.db"
[prometheus]enabled = true
[[devices]]address = "Aranet4 XXXXX"alias = "office"poll_interval = 60Security Configuration
Section titled “Security Configuration”For production deployments, enable API authentication and rate limiting:
[security]api_key_enabled = trueapi_key = "your-secure-random-key-at-least-32-chars"rate_limit_enabled = truerate_limit_requests = 100rate_limit_window_secs = 60See API Security for detailed configuration.
Webhook Notifications
Section titled “Webhook Notifications”Configure HTTP POST alerts when sensor thresholds are exceeded:
[webhooks]enabled = trueco2_threshold = 1000 # ppmradon_threshold = 300 # Bq/m³battery_threshold = 10 # percentcooldown_secs = 300 # seconds between repeat alerts per device
[[webhooks.endpoints]]url = "https://hooks.slack.com/services/T00/B00/xxx"events = ["co2_high", "radon_high", "battery_low"]
[[webhooks.endpoints]]url = "https://ntfy.sh/my-aranet-alerts"events = ["co2_high"]headers = { "Authorization" = "Bearer tk_xxx" }Each webhook POST sends a JSON payload:
{ "event": "co2_high", "device_id": "Aranet4 AAAAA", "alias": "office", "value": 1200.0, "threshold": 1000.0, "unit": "ppm", "reading": { ... }, "timestamp": "2026-03-28T12:00:00Z"}Valid event types: co2_high, radon_high, battery_low.
InfluxDB Export
Section titled “InfluxDB Export”Export sensor readings to InfluxDB v2 in real time:
[influxdb]enabled = trueurl = "http://localhost:8086"token = "your-influxdb-token"org = "my-org"bucket = "aranet"measurement = "aranet"precision = "s" # s, ms, us, or nsData is written using the InfluxDB v2 line protocol: aranet,device=office,address=Aranet4+AAAAA co2=450i,temperature=22.50,humidity=45i,pressure=1013.50,battery=85i.
mDNS Service Discovery
Section titled “mDNS Service Discovery”When running, aranet-service automatically advertises itself on the local network via mDNS:
- Service type:
_aranet._tcp.local. - HTTP service:
_http._tcp.local. - Properties:
version,path(/api),dashboard(/dashboard)
Clients can discover the service without manual IP configuration:
# macOSdns-sd -B _aranet._tcp local.
# Linux (avahi)avahi-browse -r _aranet._tcpGrafana Dashboard
Section titled “Grafana Dashboard”A pre-built Grafana dashboard is included at grafana/aranet-dashboard.json. To import:
- Open Grafana and navigate to Dashboards > Import
- Click Upload JSON file and select
grafana/aranet-dashboard.json - Select your Prometheus data source and click Import
The dashboard includes panels for:
- CO₂ gauges with threshold coloring
- Environment time series (temperature, humidity, pressure)
- Radon and radiation displays
- Battery levels per device
- Collector statistics (poll success/failure, duration)
Environment Variables
Section titled “Environment Variables”# Log levelexport RUST_LOG=aranet_service=info
# Config file locationexport ARANET_CONFIG=/path/to/server.tomlPrometheus Integration
Section titled “Prometheus Integration”Scrape Configuration
Section titled “Scrape Configuration”Add to prometheus.yml:
scrape_configs: - job_name: 'aranet' static_configs: - targets: ['localhost:8080'] metrics_path: /metrics scrape_interval: 60sAvailable Metrics
Section titled “Available Metrics”| Metric | Type | Description |
|---|---|---|
aranet_co2_ppm | gauge | CO₂ concentration |
aranet_temperature_celsius | gauge | Temperature |
aranet_humidity_percent | gauge | Relative humidity |
aranet_pressure_hpa | gauge | Atmospheric pressure |
aranet_battery_percent | gauge | Battery level |
aranet_reading_age_seconds | gauge | Time since last reading |
aranet_collector_running | gauge | Collector status (1/0) |
aranet_collector_uptime_seconds | gauge | Collector uptime |
aranet_device_poll_success_total | counter | Successful polls |
aranet_device_poll_failure_total | counter | Failed polls |
aranet_device_poll_duration_ms | gauge | Per-device poll duration |
Multi-Device Setup
Section titled “Multi-Device Setup”Multiple Sensors
Section titled “Multiple Sensors”Configure multiple devices in server.toml:
[[devices]]address = "Aranet4 AAAAA"alias = "office"poll_interval = 60
[[devices]]address = "Aranet4 BBBBB"alias = "bedroom"poll_interval = 120
[[devices]]address = "Aranet4 CCCCC"alias = "kitchen"poll_interval = 60
[[devices]]address = "AranetRn+ DDDDD"alias = "basement"poll_interval = 300 # Radon updates slowlyMulti-Location Deployment
Section titled “Multi-Location Deployment”For sensors spread across multiple locations:
- Option A: Central collector - One aranet-service with MQTT forwarding
- Option B: Distributed collectors - Multiple aranet-service instances pushing to central Prometheus
┌─────────────────┐ ┌─────────────────┐│ Location A │ │ Location B ││ aranet-service │────▶│ aranet-service ││ :8080 │ │ :8080 │└────────┬────────┘ └────────┬────────┘ │ │ └──────────┬────────────┘ ▼ ┌────────────────┐ │ Prometheus │ │ (Central) │ └───────┬────────┘ │ ▼ ┌────────────────┐ │ Grafana │ └────────────────┘Monitoring & Logging
Section titled “Monitoring & Logging”View Logs
Section titled “View Logs”# systemdjournalctl -u aranet -f
# launchdtail -f /usr/local/var/log/aranet.log
# Dockerdocker logs -f aranet-serviceHealth Check
Section titled “Health Check”# Basic health checkcurl http://localhost:8080/api/health# {"status":"ok","version":"0.2.0","timestamp":"2026-03-28T10:30:00Z"}
# Detailed health check with diagnosticscurl http://localhost:8080/api/health/detailed# Returns database, collector, and platform diagnosticsPrometheus Metrics
Section titled “Prometheus Metrics”curl http://localhost:8080/metricsTroubleshooting
Section titled “Troubleshooting”Service Won’t Start
Section titled “Service Won’t Start”- Check logs for errors
- Verify configuration file syntax:
toml-cli check server.toml - Ensure Bluetooth permissions (Linux: CAP_NET_ADMIN)
No Bluetooth Access
Section titled “No Bluetooth Access”- Linux: Add user to
bluetoothgroup:sudo usermod -aG bluetooth $USER - macOS: Grant Bluetooth permission in System Preferences
- Docker: Run with
--privilegedor mount/var/run/dbus
Connection Timeouts
Section titled “Connection Timeouts”- Check device is powered on and in range
- Increase poll_interval (device may be busy)
- Verify no other app is connected to the device