Manual installation

If you prefer not to use the CLI, HomeCmdr can be installed manually via Docker or as a systemd service built from source.

Prerequisites

  • Docker Engine and Docker Compose
  • Network access to your adapters (MQTT broker, local device IPs, etc.)

Quick start

# Clone the repository
git clone https://github.com/homecmdr/homecmdr-api homecmdr
cd homecmdr

# Copy and edit the config
cp config/default.toml config/local.toml
$EDITOR config/local.toml   # set auth.master_key, adapter settings, etc.

# Build and start
docker compose up -d

The API listens on http://localhost:3001 by default.

docker compose logs -f homecmdr   # stream logs
docker compose down               # stop

What the compose file does

SettingValue
Restart policyunless-stopped — survives reboots
Config volume./config bind-mounted read-only at /config
Data volumenamed Docker volume homecmdr-data at /data
Env varsHOMECMDR_CONFIG, HOMECMDR_DATA_DIR

Storing the master key outside the config file

Create a .env file alongside docker-compose.yml (do not commit this file):

HOMECMDR_MASTER_KEY=your-real-secret-key

Then uncomment the HOMECMDR_MASTER_KEY line in docker-compose.yml.

Updating

docker compose pull     # if using a published image
# or
docker compose build    # rebuild from source
docker compose up -d

The SQLite database in the named volume is preserved across updates.


Bare metal / systemd

1. Build the binary

git clone https://github.com/homecmdr/homecmdr-api homecmdr
cd homecmdr
cargo build --release -p api
sudo cp target/release/api /usr/local/bin/homecmdr

2. Create the system user and directories

sudo useradd --system --no-create-home --shell /sbin/nologin homecmdr
sudo mkdir -p /etc/homecmdr /var/lib/homecmdr
sudo chown homecmdr:homecmdr /var/lib/homecmdr

3. Install the config

sudo cp config/default.toml /etc/homecmdr/default.toml
sudo $EDITOR /etc/homecmdr/default.toml   # set auth.master_key, adapters, etc.
sudo chmod 640 /etc/homecmdr/default.toml
sudo chown root:homecmdr /etc/homecmdr/default.toml

Copy Lua asset directories if you use scenes, automations, or scripts:

sudo cp -r config/scenes      /etc/homecmdr/scenes
sudo cp -r config/automations /etc/homecmdr/automations
sudo cp -r config/scripts     /etc/homecmdr/scripts
sudo chown -R homecmdr:homecmdr /etc/homecmdr/scenes \
    /etc/homecmdr/automations /etc/homecmdr/scripts

Update the directory paths in /etc/homecmdr/default.toml:

[scenes]
directory = "/etc/homecmdr/scenes"

[automations]
directory = "/etc/homecmdr/automations"

[scripts]
directory = "/etc/homecmdr/scripts"

4. Store the master key securely (optional)

Instead of putting the key in the config file, use a systemd EnvironmentFile:

sudo tee /etc/homecmdr/secrets.env > /dev/null <<'EOF'
HOMECMDR_MASTER_KEY=your-real-key-here
EOF
sudo chmod 600 /etc/homecmdr/secrets.env
sudo chown root:homecmdr /etc/homecmdr/secrets.env

Then uncomment the EnvironmentFile line in the unit file (step 5 below).

5. Install and start the systemd unit

sudo cp deploy/homecmdr.service /etc/systemd/system/homecmdr.service
sudo systemctl daemon-reload
sudo systemctl enable --now homecmdr

Check status and logs:

sudo systemctl status homecmdr
sudo journalctl -u homecmdr -f

Updating the binary

cargo build --release -p api
sudo cp target/release/api /usr/local/bin/homecmdr
sudo systemctl restart homecmdr

First login

Regardless of deployment method, verify the server is up:

curl -s http://localhost:3001/health
# {"status":"ok"}

All endpoints except /health and /ready require a Bearer token. Use your master key to authenticate:

curl -s -H "Authorization: Bearer YOUR_MASTER_KEY" http://localhost:3001/devices

Create a scoped API key for scripts and automations so they do not use your master key directly:

curl -s -X POST http://localhost:3001/auth/keys \
  -H "Authorization: Bearer YOUR_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label": "home-scripts", "role": "write"}'

See Authentication for the full key management reference.


Running a local dev instance

Run a second instance alongside your primary one — useful for testing config changes without affecting the live system.

Create a separate config:

cp config/default.toml config/dev.toml

Edit config/dev.toml to use a different port and database:

[api]
bind_address = "127.0.0.1:3002"

[persistence]
database_url = "sqlite://data/dev-homecmdr.db"

[adapters.zigbee2mqtt]
client_id = "homecmdr-dev"   # avoids MQTT session conflicts

Run it:

HOMECMDR_CONFIG=config/dev.toml cargo run -p api

The dev instance is fully independent: separate port, separate database, separate MQTT session.