Self-hosting NervesHub
Run your own NervesHub: architecture, Docker, PostgreSQL, object storage, optional ClickHouse, and the core environment variables.
Written By Josh Kalderimis
Last updated About 2 months ago
NervesHub is designed to be self-hosted. This guide covers what it needs to run, how the pieces fit together, and the core configuration to get an instance online.
Prefer not to run infrastructure yourself? Use the hosted NervesCloud service instead β see NervesHub vs NervesCloud.
Architecture at a glance
A NervesHub deployment is made of a few parts:
Application server β the Elixir/Phoenix app. It serves the web UI and HTTP API, and terminates device websocket connections. Pre-built Docker images are published to the GitHub Container Registry.
PostgreSQL β the primary database. NervesHub is developed against PostgreSQL 18.
S3-compatible object storage β stores firmware and archive files.
ClickHouse (optional) β stores device logs and powers analytics/Insights. Leave it out and the rest of the platform runs exactly the same.
The app exposes two endpoints: a web endpoint for users (web UI + HTTP API) and a device endpoint for device connections. These can run on different hosts and ports, and the device endpoint typically uses mutual TLS for certificate-based device auth.
Local trial with Docker Compose
The repository includes a docker-compose.yml that starts the backing services (PostgreSQL and ClickHouse) for local development:
services:
postgres:
image: postgres:18
restart: always
environment:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
clickhouse:
image: clickhouse/clickhouse-server:25.4.2.31
environment:
CLICKHOUSE_SKIP_USER_SETUP: 1
ports:
- "8123:8123"
- "9000:9000"This gives you the databases; you then run the application server (from a released Docker image or from source) pointed at them. For anything beyond a local trial, run PostgreSQL, object storage, and ClickHouse as managed/persistent services rather than throwaway containers.
Core configuration
NervesHub is configured entirely through environment variables at runtime. The most important ones are below. See the Self-hosting configuration reference for the complete list.
Secrets
Web endpoint
Device endpoint
Database
Firmware & archive storage
Optional: device logs & analytics (ClickHouse)
Optional: email, auth, and observability
The exact required vs. optional set can change between releases. Always cross-check against
config/runtime.exsin the version you're deploying.
First run
Provision PostgreSQL and (optionally) ClickHouse and object storage.
Set the environment variables above.
Start the application image. With
DATABASE_AUTO_MIGRATORenabled, the schema is created on boot; otherwise run migrations manually.Open the web endpoint, create your first user, organization, and product, then follow the Quickstart from step 2 onward β pointing your device and CLI at your own host instead of NervesCloud.
Production considerations
TLS everywhere. Terminate TLS on both endpoints. The device endpoint needs a certificate chain compatible with your device certificate strategy.
Persistent storage. Back PostgreSQL, ClickHouse, and object storage with durable, backed-up storage.
Clustering & the orchestrator. NervesHub's deployment orchestrator is designed to run across a cluster; review the deployment-tuning variables in the configuration reference before scaling out.
Draining connections. Device socket drainer settings control how connections are shed during deploys β tune them for your fleet size.