Skip to content

ptr727/ESPHome-NonRoot

ESPHome-NonRoot

ESPHome docker container that supports non-root operation.

Build

Code and pipeline are on GitHub.
Docker image is published on Docker Hub.
Image is rebuilt on the weekly schedule and on demand, picking up the latest tracked ESPHome release and upstream container updates.

Status

Last Commit
Workflow Status
Last Build
Docker Latest

Release Notes

  • Version 1.7:
    • Migrated the dashboard to ESPHome's new esphome-device-builder package (#60).
    • Switched the image build to a uv virtual environment.
    • Pinned and auto-tracked the esphome-device-builder version alongside esphome.
    • Removed the no-op ESPHOME_DASHBOARD_USE_PING setting; device-builder always uses mDNS with a ping fallback.

See Release History for complete release notes and older versions.

Usage

Configuration

  • volumes :
    • /config : Volume mapping to project files.
    • /cache (Optional) : Volume mapping to runtime generated content.
    • /tmp (Optional) : Volume mapping for temp files.
  • user (Optional) : Run the container under the specified user account.
    • Use the uid:gid notation, e.g. user: 1001:100.
      • Get the uid : sudo id -u nonroot.
      • Get the gid : sudo id -g nonroot.
    • Use an existing user or create a system account on the host.
      • adduser --no-create-home --disabled-password --system --group users nonroot.
    • Omitting the user option will run under default root account.
    • Make sure the container user has permissions to the mapped /config and /cache volumes.
      • sudo chown -R nonroot:users /data/esphome
      • sudo chmod -R ug=rwx,o=rx /data/esphome
  • environment :
    • ESPHOME_VERBOSE (Optional) : Enables verbose log output, e.g. ESPHOME_VERBOSE=true.
    • TZ (Optional) : Sets the timezone, e.g. TZ=America/Los_Angeles, default is Etc/UTC.

Docker Compose Dashboard

services:
  esphome:
    image: docker.io/ptr727/esphome-nonroot:latest
    container_name: esphome-test
    restart: unless-stopped
    user: 1001:100
    environment:
      - TZ=America/Los_Angeles
      - ESPHOME_VERBOSE=true
    network_mode: bridge
    ports:
      - 6052:6052
    volumes:
      - /data/esphome/config:/config
      - /data/esphome/cache:/cache
    tmpfs:
      - /tmp:size=1g,mode=1777
# Create nonroot user
adduser --no-create-home --disabled-password --system --group users nonroot
id nonroot
# uid=1001(nonroot) gid=100(users) groups=100(users)

# Prepare directories for use by nonroot:users
mkdir -p /data/esphome/config /data/esphome/cache
sudo chown -R nonroot:users /data/esphome
sudo chmod -R ug=rwx,o=rx /data/esphome

# Launch stack
docker compose --file ./Docker/Compose.yml up --detach

# Open browser: http://localhost:6052
# Attach shell: docker exec -it --user 1001:100 esphome-test /bin/bash

# Destroy stack
docker compose --file ./Docker/Compose.yml down --volumes

Docker Run Command

# esphome version
docker run --rm --pull always --name esphome-test -v /data/esphome/config:/config -v /data/esphome/cache:/cache ptr727/esphome-nonroot:latest esphome version
latest: Pulling from ptr727/esphome-nonroot
Digest: sha256:8f32848551446d0420390477fccb8c833d879b640b95533f443cb623882e9688
Status: Image is up to date for ptr727/esphome-nonroot:latest
Version: 2024.5.5

Docker Run Interactive Shell

# /bin/bash
docker run --rm --user 1001:100 -it --pull always --name esphome-test -v /data/esphome/config:/config -v /data/esphome/cache:/cache ptr727/esphome-nonroot:latest /bin/bash
latest: Pulling from ptr727/esphome-nonroot
Digest: sha256:8f32848551446d0420390477fccb8c833d879b640b95533f443cb623882e9688
Status: Image is up to date for ptr727/esphome-nonroot:latest
I have no name!@012d4b62d376:/config$ id
uid=1001 gid=100(users) groups=100(users)
I have no name!@012d4b62d376:/config$

Docker Compose with Static IP and Traefik

  esphome:
    image: docker.io/ptr727/esphome-nonroot:latest
    container_name: esphome
    hostname: esphome
    domainname: ${DOMAIN_NAME}
    restart: unless-stopped
    user: ${USER_NONROOT_ID}:${USERS_GROUP_ID}
    group_add:
      - ${DOCKER_GROUP_ID}
    security_opt: # Use with care
      - seccomp=unconfined
      - apparmor=unconfined
    environment:
      - TZ=${TZ}
      # - ESPHOME_VERBOSE=true
    volumes:
      - ${APPDATA_DIR}/esphome/config:/config
      - ${APPDATA_DIR}/esphome/cache:/cache
    tmpfs:
      - /tmp:size=1g,mode=1777
    networks:
      public_network:
        ipv4_address: ${ESPHOME_IP}
        mac_address: ${ESPHOME_MAC}
      local_network:
      stack_network:
    labels:
      - traefik.enable=true
      - traefik.http.routers.esphome.rule=HostRegexp(`^esphome${DOMAIN_REGEX}$$`)
      - traefik.http.services.esphome.loadbalancer.server.scheme=http
      - traefik.http.services.esphome.loadbalancer.server.port=6052

Use Case

  • Running containers as non-privileged and as non-root is a docker best practice.
  • The official ESPHome docker container does not support running as a non-root user.
  • Issue analysis based on ESPHome 2024.5.5 (the upstream version analyzed when this project was created) Dockerfile:
    • PLATFORMIO_GLOBALLIB_DIR=/piolibs sets the PlatformIO globallib_dir option to /piolibs.
      • /piolibs is not mapped to an external volume.
      • /piolibs has default permissions and requires root write permissions at runtime.
      • The globallib_dir option has been deprecated.
        • This option is DEPRECATED. We do not recommend using global libraries for new projects. Please use a declarative approach for the safety-critical embedded development and declare project dependencies using the lib_deps option.
    • platformio_install_deps.py installs global libraries using pio pkg install -g, the -g option has been deprecated.
      • We DO NOT recommend installing libraries in the global storage. Please use the lib_deps option and declare library dependencies per project.
    • The presence of a /cache directory changes pio_cache_base to /cache/platformio, the default is /config/.esphome/platformio
      • PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms", PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages", and PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache" is explicitly set as child directories of pio_cache_base.
      • It is simpler to set PLATFORMIO_CORE_DIR PlatformIO core_dir option, and not setting PLATFORMIO_PLATFORMS_DIR platforms_dir, PLATFORMIO_PACKAGES_DIR packages_dir, and PLATFORMIO_CACHE_DIR cache_dir options, that are by default child directories of core_dir.
    • The presence of a /build directory sets the ESPHOME_BUILD_PATH environment variable, that sets the CONF_BUILD_PATH ESPHome build_path option, the default is /config/.esphome/build.
      • The directory presence detection could override an explicitly set ESPHOME_BUILD_PATH option.
    • ESPHOME_DATA_DIR can be used to set the ESPHome data_dir intermediate build output directory, the default is /config/.esphome, or hardcoded to /data for the HA addon image.
    • PLATFORMIO_CORE_DIR PlatformIO core_dir option is not set and defaults to ~/.platformio.
    • PIP_CACHE_DIR is not set and defaults to ~/.cache/pip.
    • HOME (~) is not set and defaults to e.g. /home/[username] or / or /nonexistent that either does not exists or the executing user does not have write permissions.

Project Design

  • Use Python docker base image simplifying use for Python in a container environment.
  • Use a multi-stage build minimizing size and layer complexity of the final stage.
  • Use uv to build a virtual environment in the builder stage, and copy the self-contained environment into the slim final stage.
  • Set appropriate PlatformIO and ESPHome environment variables to store projects in /config and dynamic and temporary content in /cache volumes.
  • Refer to Dockerfile for container details.
  • Refer to publish-release.yml (the publisher) and build-docker-task.yml (the image build) for pipeline details.

Debugging

The included Dev Container can be used for ESPHome Python or PlatformIO C++ debugging in VSCode.

Detailed debug setup details are beyond the scope of this project, refer to my ESPHome-Config project for slightly more complete debugging setup instructions.

License

Licensed under the MIT License
GitHub License

About

ESPHome docker container that supports non-root operation

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Contributors