ESPHome docker container that supports non-root operation.
Licensed under the MIT License
Code and Pipeline is on GitHub.
Docker image is published on Docker Hub.
Image is rebuilt weekly, or when a new ESPHome version is released, picking up the latest ESPHome release and upstream container updates.
- Version 1.5:
- Using Python 3.13 base image.
 
- Version 1.4:
- Removed custom handling for ESPHOME_VERBOSEenabling--verbose, PR merged.
 
- Removed custom handling for 
- Version 1.3:
- Version 1.2:
- Delete temp directory contents and prune PIO cached content on startup.
- Added Dev Container that can be used for ESPHome or PlatformIO debugging.
 
- Version 1.1:
- Added daily actions job to trigger a build if the ESPHome version changed.
 
- Version 1.0:
- Initial public release.
 
- volumes:- /config: Volume mapping to project files, e.g.- /data/esphome/config:/config.
- /cache: Volume mapping to runtime generated content, e.g.- /data/esphome/cache:/cache.
 
- user(Optional) : Run the container under the specified user account.- Use the uid:gidnotation, e.g.user: 1001:100.- Get the uid:sudo id -u nonroot.
- Get the gid:sudo id -g nonroot.
 
- Get the 
- Use an existing user or create a system account on the host.
- adduser --no-create-home --disabled-password --system --group users nonroot.
 
- Omitting the useroption will run under defaultrootaccount.
- Make sure the container user has permissions to the mapped /configand/cachevolumes.- sudo chown -R nonroot:users /data/esphome
- sudo chmod -R ug=rwx,o=rx /data/esphome
 
 
- Use the 
- environment:- ESPHOME_VERBOSE(Optional) : Enables verbose log output, e.g.- ESPHOME_VERBOSE=true.
- ESPHOME_DASHBOARD_USE_PING(Optional) : Use- pinginstead of- mDNSto test if nodes are up, e.g.- ESPHOME_DASHBOARD_USE_PING=true.
- TZ(Optional) : Sets the timezone, e.g.- TZ=America/Los_Angeles, default is- Etc/UTC.
 
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
      - ESPHOME_DASHBOARD_USE_PING=true
    network_mode: bridge
    ports:
      - 6052:6052
    volumes:
      - /data/esphome/config:/config
      - /data/esphome/cache:/cache# 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# 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 versionlatest: Pulling from ptr727/esphome-nonroot
Digest: sha256:8f32848551446d0420390477fccb8c833d879b640b95533f443cb623882e9688
Status: Image is up to date for ptr727/esphome-nonroot:latest
Version: 2024.5.5# /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/bashlatest: 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$- 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(current version as of writing)Dockerfile:- PLATFORMIO_GLOBALLIB_DIR=/piolibssets the PlatformIO- globallib_diroption to- /piolibs.- /piolibsis not mapped to an external volume.
- /piolibshas default permissions and requires- rootwrite permissions at runtime.
- The globallib_diroption 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.pyinstalls global libraries using- pio pkg install -g, the- -goption 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 /cachedirectory changespio_cache_baseto/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_DIRPlatformIOcore_diroption, and not settingPLATFORMIO_PLATFORMS_DIRplatforms_dir,PLATFORMIO_PACKAGES_DIRpackages_dir, andPLATFORMIO_CACHE_DIRcache_diroptions, that are by default child directories ofcore_dir.
 
- The presence of a /builddirectory sets theESPHOME_BUILD_PATHenvironment variable, that sets theCONF_BUILD_PATHESPHomebuild_pathoption, the default is/config/.esphome/build.- The directory presence detection could override an explicitly set ESPHOME_BUILD_PATHoption.
 
- The directory presence detection could override an explicitly set 
- ESPHOME_DATA_DIRcan be used to set the ESPHome- data_dirintermediate build output directory, the default is- /config/.esphome, or hardcoded to- /datafor the HA addon image.
- PLATFORMIO_CORE_DIRPlatformIO- core_diroption is not set and defaults to- ~/.platformio.
- PIP_CACHE_DIRis not set and defaults to- ~/.cache/pip.
- HOME(- ~) is not set and defaults to e.g.- /home/[username]or- /or- /nonexistentthat either does not exists or the executing user does not have write permissions.
 
- 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.
- Build wheelarchives for the platform in the builder stage, and install the platform specific generated wheel packages in the final stage.
- Set appropriate PlatformIO and ESPHome environment variables to store projects in /configand dynamic and temporary content in/cachevolumes.
- Refer to Dockerfilefor container details.
- Refer to BuildDockerPush.ymland for pipeline details.
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.
- ESPHome's DockerfileinstallsLIB_DEPSandBUILD_DEPS, that should not be required when installing usingwheel, right?
- Using unversioned base Python docker images will use the current released version of Python, and may not always be compatible with ESPHome.
- Alpine uses muslnotglibc,gcompatis required for PIO and tools that do not have nativemuslsupport.
- Track open PR for a general LOG_LEVELconfiguration vs. usingESPHOME_VERBOSE.
- Chance of a version mismatch when tagging the docker image with the current ESPHome version vs. the version actually installed when building the image.