ThingsBoard is an IoT platform for device management, data collection, visualization, and processing. It's the heaviest of the three to run — plan your VPS sizing accordingly.
Target environment: Ubuntu 24.04 LTS, 4 vCPU / 8 GB RAM minimum for a real workload (it will limp along on 4 GB for testing only), 40 GB+ disk. This guide uses the official single-node Docker Compose setup with PostgreSQL.
1. Initial server prep
apt update && apt upgrade -y
apt install -y curl ufw
adduser deploy
usermod -aG sudo deploy
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 1883/tcp # MQTT
ufw allow 5683/udp # CoAP
ufw allow 7070/tcp # optional: direct app access during setup
ufw enableRe-login as deploy.
2. Install Docker
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker3. Set kernel/vm settings ThingsBoard expects
ThingsBoard's bundled Postgres/Cassandra-adjacent services want a higher vm.max_map_count:
sudo sysctl -w vm.max_map_count=262144
echo 'vm.max_map_count=262144' | sudo tee -a /etc/sysctl.conf4. Set up the project
mkdir -p ~/thingsboard
cd ~/thingsboard
mkdir -p .mytb-data .mytb-logs
sudo chown -R 799:799 .mytb-data .mytb-logs(UID/GID 799 is the user ThingsBoard's container runs as internally — matching ownership avoids permission errors on the volumes.)
5. Create the Compose file
nano docker-compose.ymlservices:
postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_DB: thingsboard
POSTGRES_PASSWORD: "CHANGE_ME_DB_PASSWORD"
volumes:
- ./.postgres-data:/var/lib/postgresql/data
networks: [tb]
mytb:
image: thingsboard/tb-postgres:latest
restart: unless-stopped
depends_on: [postgres]
ports:
- "127.0.0.1:8080:9090"
- "1883:1883"
- "7070:7070"
- "5683-5688:5683-5688/udp"
environment:
TB_QUEUE_TYPE: in-memory
SPRING_DATASOURCE_URL: "jdbc:postgresql://postgres:5432/thingsboard"
SPRING_DATASOURCE_USERNAME: postgres
SPRING_DATASOURCE_PASSWORD: "CHANGE_ME_DB_PASSWORD"
volumes:
- ./.mytb-data:/data
- ./.mytb-logs:/var/log/thingsboard
networks: [tb]
networks:
tb:
driver: bridgeReplace CHANGE_ME_DB_PASSWORD in both places with a real password. Note port 8080 (the web UI) is bound to localhost only — Nginx will expose it publicly. MQTT (1883), the device provisioning port (7070), and CoAP (5683/udp) are left open directly since IoT devices will connect to those.
6. Run first-time install, then start the stack
ThingsBoard's image needs an install/upgrade step run once before first launch:
docker compose run --rm mytb sh -c "/usr/share/thingsboard/bin/install/install.sh --loadDemo"Drop --loadDemo if you don't want sample dashboards/devices seeded. This step can take a few minutes.
Then start the full stack:
docker compose up -d
docker compose logs -f mytbWait for a log line indicating the server has started, then Ctrl+C.
7. Put Nginx + TLS in front of the web UI
sudo apt install -y nginx certbot python3-certbot-nginx
sudo nano /etc/nginx/sites-available/thingsboardserver {
listen 80;
server_name tb.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s;
}
}sudo ln -s /etc/nginx/sites-available/thingsboard /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d tb.yourdomain.com8. First login
Visit https://tb.yourdomain.com. Default credentials (change these immediately):
- System Administrator:
sysadmin@thingsboard.org/sysadmin - Tenant Administrator:
tenant@thingsboard.org/tenant - Customer User:
customer@thingsboard.org/customer
Log in as sysadmin first and change all default passwords before connecting any real devices.
9. Connecting a test device (sanity check)
Using MQTT from the VPS itself or another machine:
mosquitto_pub -d -q 1 -h tb.yourdomain.com -p 1883 \
-t "v1/devices/me/telemetry" \
-u "DEVICE_ACCESS_TOKEN" \
-m "{\"temperature\": 22.5}"Grab a device access token from Tenant Admin → Devices → (create a device) → Copy access token. Confirm the telemetry shows up on the device's "Latest telemetry" tab.
10. Backups
Back up:
~/thingsboard/.postgres-data(all entities, users, device metadata, and — if using default in-memory queue with Postgres storage — telemetry too)~/thingsboard/.mytb-data(attributes/cache data ThingsBoard itself manages)
docker compose stop mytb # avoid backing up mid-write; postgres can stay up briefly
tar -czf tb-backup-$(date +%F).tar.gz -C ~/thingsboard .postgres-data .mytb-data
docker compose start mytbFor a production deployment with meaningful telemetry volume, consider migrating to Cassandra or TimescaleDB for the timeseries store rather than plain Postgres — the default setup here is fine for moderate device counts but won't scale as gracefully.
11. Updating
cd ~/thingsboard
docker compose pull
docker compose stop mytb
docker compose run --rm mytb sh -c "/usr/share/thingsboard/bin/install/install.sh --upgrade"
docker compose up -dAlways check the ThingsBoard upgrade notes for your specific version jump — some releases require intermediate upgrade steps rather than jumping straight to latest.
