diff --git a/ziggurat/DOCS.md b/ziggurat/DOCS.md
index 2c4a599..734c28a 100644
--- a/ziggurat/DOCS.md
+++ b/ziggurat/DOCS.md
@@ -47,7 +47,50 @@ The stack's logging verbosity (`error`, `warn`, `info`, `debug`, `trace`).
`debug` logs every frame on the network and is very chatty; `info` is
appropriate for normal operation.
-## Building
-
-The first build will take a while, especially on older hardware. Subsequent builds use a
-build cache and will be very fast.
+## Running standalone (plain Docker)
+
+The same image runs outside Home Assistant as an ordinary container. When
+`/data/options.json` is absent, configuration is read from environment variables
+instead of the add-on options:
+
+
+docker run | docker-compose.yml |
+|
+
+```bash
+docker run --rm \
+ --device /dev/serial/by-id/usb-...-if00-port0 \
+ --cap-add SYS_NICE \
+ -p 9999:9999 \
+ -e ZIGGURAT_DEVICE=/dev/serial/by-id/usb-...-if00-port0 \
+ -e ZIGGURAT_BAUDRATE=460800 \
+ -e ZIGGURAT_FLOW_CONTROL=hardware \
+ -e ZIGGURAT_LOG_LEVEL=info \
+ ghcr.io/zigpy/ziggurat-addon
+```
+
+ |
+
+```yaml
+services:
+ ziggurat:
+ image: ghcr.io/zigpy/ziggurat-addon
+ restart: unless-stopped
+ devices:
+ - /dev/serial/by-id/usb-...-if00-port0
+ cap_add:
+ - SYS_NICE
+ ports:
+ - "9999:9999"
+ environment:
+ ZIGGURAT_DEVICE: /dev/serial/by-id/usb-...-if00-port0
+ ZIGGURAT_BAUDRATE: "460800"
+ ZIGGURAT_FLOW_CONTROL: hardware
+ ZIGGURAT_LOG_LEVEL: info
+```
+
+ |
+
+
+`--cap-add SYS_NICE` lets the stack raise its scheduling priority. It is optional but
+recommended.
diff --git a/ziggurat/rootfs/etc/s6-overlay/s6-rc.d/ziggurat/run b/ziggurat/rootfs/etc/s6-overlay/s6-rc.d/ziggurat/run
index c2ae46a..bae1a91 100755
--- a/ziggurat/rootfs/etc/s6-overlay/s6-rc.d/ziggurat/run
+++ b/ziggurat/rootfs/etc/s6-overlay/s6-rc.d/ziggurat/run
@@ -8,19 +8,42 @@ declare device
declare baudrate
declare flow_control
declare log_level
+declare listen
-device=$(bashio::config 'device')
-baudrate=$(bashio::config 'baudrate' '460800')
-flow_control=$(bashio::config 'flow_control' 'hardware')
-log_level=$(bashio::config 'log_level' 'info')
+if bashio::fs.file_exists '/data/options.json'; then
+ # Addon mode: Supervisor provides /data/options.json
+ device=$(bashio::config 'device')
+ baudrate=$(bashio::config 'baudrate' '460800')
+ flow_control=$(bashio::config 'flow_control' 'hardware')
+ log_level=$(bashio::config 'log_level' 'info')
+else
+ # Plain Docker mode: configuration comes from the environment variables
+ device="${ZIGGURAT_DEVICE}"
+ baudrate="${ZIGGURAT_BAUDRATE:-460800}"
+ flow_control="${ZIGGURAT_FLOW_CONTROL:-hardware}"
+ log_level="${ZIGGURAT_LOG_LEVEL:-info}"
+fi
+
+listen="${ZIGGURAT_LISTEN:-0.0.0.0:9999}"
bashio::log.info "Starting ziggurat on ${device}..."
-# Win scheduling contests against batch workloads (database, transcoding) on a
-# loaded hub: frame turnaround latency feeds directly into perceived response
-exec nice -n -10 /usr/bin/ziggurat \
- --device "${device}" \
- --baudrate "${baudrate}" \
- --flow-control "${flow_control}" \
- --listen "0.0.0.0:9999" \
+declare -a args=(
+ --device "${device}"
+ --baudrate "${baudrate}"
+ --flow-control "${flow_control}"
+ --listen "${listen}"
--log-level "${log_level}"
+)
+
+export RUST_BACKTRACE=full
+
+# Negative nice needs CAP_SYS_NICE, granted by the addon via `privileged: SYS_NICE`.
+# A bare `docker run` might not have it, so fall back to normal priority instead of
+# refusing to start.
+if nice -n -10 true 2>/dev/null; then
+ exec nice -n -10 /usr/bin/ziggurat "${args[@]}"
+else
+ bashio::log.warning "CAP_SYS_NICE not available; running at normal priority."
+ exec /usr/bin/ziggurat "${args[@]}"
+fi