Deployment & Operations¶
Guides for running the engine in development, Docker, and production environments.
Development¶
git clone https://github.com/Supporterino/TypeScript-Home-Automation.git
cd TypeScript-Home-Automation
bun install
cp .env.example .env
# Edit .env with your MQTT broker details
bun run dev # Hot-reload — restarts on file changes
bun run dev uses bun --watch which monitors all imported files and restarts automatically on save.
Docker (standalone)¶
The repo includes a Dockerfile and docker-compose.yml that run the engine alongside a Mosquitto MQTT broker.
Quick start¶
bun run docker:build # Build the image
bun run docker:up # Start engine + Mosquitto
bun run docker:down # Stop
Exposing ports¶
The default docker-compose.yml does not expose the engine's HTTP port to the host. To access the web UI, debug API, or health probes from outside the container, add a ports mapping:
Environment variables¶
Pass configuration via the environment section in docker-compose.yml:
environment:
- TZ=Europe/Berlin
- MQTT_HOST=mosquitto
- HTTP_PORT=8080
- HTTP_TOKEN=my-secret-token
- WEB_UI_ENABLED=true
- STATE_PERSIST=true
- STATE_FILE_PATH=/data/state.json
- DEVICE_REGISTRY_ENABLED=true
- DEVICE_REGISTRY_PERSIST=true
- DEVICE_REGISTRY_FILE_PATH=/data/device-registry.json
Persistent data¶
Mount a volume for state and device registry persistence so data survives container restarts:
services:
home-automation:
volumes:
- ha-data:/data
environment:
- STATE_FILE_PATH=/data/state.json
- DEVICE_REGISTRY_FILE_PATH=/data/device-registry.json
volumes:
ha-data:
Docker (consumer package)¶
If you use ts-home-automation as an npm package in your own project, create a minimal Dockerfile:
FROM oven/bun:1
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production
COPY src ./src
COPY tsconfig.json ./
EXPOSE 8080
CMD ["bun", "run", "src/index.ts"]
Pair it with a docker-compose.yml that provides the MQTT broker and any configuration you need.
Kubernetes¶
The engine exposes health probes that integrate with Kubernetes pod lifecycle management.
Pod manifest¶
apiVersion: v1
kind: Pod
metadata:
name: home-automation
spec:
containers:
- name: engine
image: your-registry/home-automation:latest
ports:
- containerPort: 8080
env:
- name: MQTT_HOST
value: mosquitto.default.svc.cluster.local
- name: HTTP_TOKEN
valueFrom:
secretKeyRef:
name: ha-secrets
key: http-token
- name: STATE_PERSIST
value: "true"
- name: STATE_FILE_PATH
value: /data/state.json
volumeMounts:
- name: data
mountPath: /data
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /readyz
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: data
persistentVolumeClaim:
claimName: ha-data
Probe details¶
| Probe | Path | Behaviour |
|---|---|---|
| Liveness | GET /healthz |
Always returns 200 — confirms the process is alive |
| Readiness | GET /readyz |
Returns 200 only when MQTT is connected and the engine has finished starting |
Both probes are unauthenticated — no HTTP_TOKEN required.
See Health Probes for response format details.
Production checklist¶
Security¶
- Set
HTTP_TOKENto a strong, random value. Without it, the debug API and web UI are publicly accessible. - Never expose port 8080 directly to the internet. Use a reverse proxy (nginx, Caddy, Traefik) with TLS termination.
- Use environment variables or secrets for sensitive configuration (MQTT credentials, API keys, tokens). Never commit
.envfiles.
Persistence¶
- Enable
STATE_PERSIST=trueso automations retain their state across restarts. - Enable
DEVICE_REGISTRY_PERSIST=trueso the device list is available immediately on cold start (before Zigbee2MQTT sends the retainedbridge/devicesmessage). - Back up
state.jsonanddevice-registry.jsonperiodically — they are plain JSON files.
Logging¶
The engine uses pino for structured logging:
- Development: Pretty-printed to stdout (when
NODE_ENVis notproduction) - Production: Newline-delimited JSON to stdout (when
NODE_ENV=production)
Pipe stdout to your preferred log aggregator:
# Pipe to a file
bun run src/standalone.ts 2>&1 | tee /var/log/home-automation.log
# Pipe to a log shipper (e.g., Vector, Fluent Bit)
bun run src/standalone.ts | vector --config vector.toml
Log levels (set via LOG_LEVEL):
| Level | Numeric | Use case |
|---|---|---|
trace |
10 | Verbose debugging (MQTT message dispatch, state comparisons) |
debug |
20 | Development-time diagnostics |
info |
30 | Normal operation (default) |
warn |
40 | Recoverable issues (missing optional services, connection retries) |
error |
50 | Failures that need attention |
fatal |
60 | Unrecoverable errors |
Every log line includes structured fields for filtering:
{"level":30,"time":1714500000000,"msg":"Motion detected","automation":"motion-light","sensor":"hallway"}
In-memory log buffer¶
The engine maintains a ring buffer of the last 2500 log entries, queryable via:
GET /api/logs?automation=motion-light&level=40&limit=100- The web UI Logs tab
ts-ha logsCLI command
This is purely in-memory — it does not persist across restarts and is not a substitute for a proper log aggregator in production.
Monitoring¶
Health endpoints¶
Use /healthz and /readyz for uptime monitoring with tools like Uptime Kuma, Pingdom, or Kubernetes probes.
MQTT broker monitoring¶
Monitor your Mosquitto broker separately:
# Check if Mosquitto is accepting connections
mosquitto_sub -h localhost -t '$SYS/broker/uptime' -C 1
Resource usage¶
The engine is single-threaded (Bun's event loop). Typical resource usage:
- Memory: 50–150 MB depending on automation count and device registry size
- CPU: Near-zero when idle; brief spikes during MQTT message bursts
- Disk: State and device registry files are small (< 1 MB typically)
Graceful shutdown¶
The engine handles SIGINT and SIGTERM for graceful shutdown. The sequence:
- All automations receive
onStop()— clear timers, release resources - Service plugins receive
onStop() - State is saved to disk (if
STATE_PERSIST=true) - Device registry is saved to disk (if
DEVICE_REGISTRY_PERSIST=true) - MQTT disconnects cleanly
- HTTP server stops
In Docker, set stop_grace_period to allow time for cleanup:
Scaling considerations¶
The engine is designed as a single-process, single-instance application. This is intentional:
- MQTT subscriptions are stateful — multiple instances would receive duplicate messages
- State store is in-memory — multiple instances would have inconsistent state
- HomeKit bridge binds to a specific port and MAC address
If you need to handle more devices or automations, scale vertically (more CPU/RAM on the same host). The engine is lightweight enough that a Raspberry Pi 4 can handle hundreds of automations and devices.
For high availability, use Docker restart policies (unless-stopped or always) or a Kubernetes Deployment with replicas: 1 and a PersistentVolumeClaim for state data.