ESPHome docker container that supports non-root operation.
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.
- Version 1.7:
- Migrated the dashboard to ESPHome's new
esphome-device-builderpackage (#60). - Switched the image build to a
uvvirtual environment. - Pinned and auto-tracked the
esphome-device-builderversion alongsideesphome. - Removed the no-op
ESPHOME_DASHBOARD_USE_PINGsetting; device-builder always uses mDNS with a ping fallback.
- Migrated the dashboard to ESPHome's new
See Release History for complete release notes and older versions.
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: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/esphomesudo chmod -R ug=rwx,o=rx /data/esphome
- Use the
environment:
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# 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$ 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- 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=/piolibssets the PlatformIOgloballib_diroption to/piolibs./piolibsis not mapped to an external volume./piolibshas default permissions and requiresrootwrite 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 usingpio 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/platformioPLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms",PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages", andPLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"is explicitly set as child directories ofpio_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 ESPHomedata_dirintermediate build output directory, the default is/config/.esphome, or hardcoded to/datafor the HA addon image.PLATFORMIO_CORE_DIRPlatformIOcore_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.
- Use
uvto 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
/configand dynamic and temporary content in/cachevolumes. - Refer to
Dockerfilefor container details. - Refer to
publish-release.yml(the publisher) andbuild-docker-task.yml(the image build) 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.
Licensed under the MIT License