From a308ddc00df1b4eea6ac710bc5b2b5266d5a2a13 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Wed, 27 May 2026 20:02:22 -0400 Subject: [PATCH 1/9] docker: add gpio and spi groups --- README.md | 11 ++++++++++- docker-compose.yml | 7 ++++++- dockerfile | 11 +++++++++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d77e60b..3d94089 100644 --- a/README.md +++ b/README.md @@ -365,7 +365,16 @@ sudo bash ./setup-radio-config.sh 4. Configure the [docker compose](./docker-compose.yml) to your specific hardware and file paths. Be sure to comment-out or delete lines that aren't required for your hardware. Please note that your hardware devices might be at a different path than those listed in the docker compose file. -5. Build and start the container. +5. If you are using SPI/GPIO hardware, make sure the `GPIO_GID` and `SPI_GID` + values match the numeric group IDs on your host. The compose file defaults + to `GPIO_GID=986` and `SPI_GID=989`. + +```bash +getent group gpio +getent group spi +``` + +6. Build and start the container. ```bash docker compose up -d --force-recreate --build diff --git a/docker-compose.yml b/docker-compose.yml index 647db59..98a862d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,9 +2,12 @@ services: pymc-repeater: build: context: . + dockerfile: dockerfile args: PUID: ${PUID:-1000} PGID: ${PGID:-1000} + GPIO_GID: ${GPIO_GID:-986} + SPI_GID: ${SPI_GID:-989} container_name: pymc-repeater restart: unless-stopped ports: @@ -20,7 +23,9 @@ services: - SYS_RAWIO # USB DEVICSE PERMISSIONS group_add: - - plugdev + - "${GPIO_GID:-986}" + - "${SPI_GID:-989}" + - plugdev volumes: - ./config:/etc/pymc_repeater - ./data:/var/lib/pymc_repeater diff --git a/dockerfile b/dockerfile index a2e1218..39e2f6d 100644 --- a/dockerfile +++ b/dockerfile @@ -5,6 +5,8 @@ ARG USER=repeater ARG GROUP=repeater ARG PUID=15888 ARG PGID=15888 +ARG GPIO_GID=986 +ARG SPI_GID=989 ARG TARGETARCH ARG YQ_VERSION=v4.40.5 @@ -16,7 +18,9 @@ ENV INSTALL_DIR=/opt/pymc_repeater \ PYTHONUNBUFFERED=1 \ SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYMC_REPEATER=${PACKAGE_VERSION} \ PUID=${PUID} \ - PGID=${PGID} + PGID=${PGID} \ + GPIO_GID=${GPIO_GID} \ + SPI_GID=${SPI_GID} # Install runtime dependencies only RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \ @@ -45,7 +49,10 @@ RUN arch="${TARGETARCH:-}" \ # Create the group and user in order to run without root privileges RUN groupadd --gid "$PGID" "$GROUP" \ - && useradd --uid "$PUID" --gid "$PGID" --home-dir "$HOME_DIR" --create-home --shell /usr/bin/bash "$USER" + && groupadd --gid "$GPIO_GID" gpio \ + && groupadd --gid "$SPI_GID" spi \ + && useradd --uid "$PUID" --gid "$PGID" --home-dir "$HOME_DIR" --create-home --shell /usr/bin/bash "$USER" \ + && usermod -a -G gpio,spi "$USER" # Create runtime directories RUN mkdir -p ${INSTALL_DIR} ${CONFIG_DIR} ${DATA_DIR} \ From 9e8c152f0b8414caa38bb7e2971e62dcc4d737a2 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Thu, 28 May 2026 14:31:59 -0400 Subject: [PATCH 2/9] docker: tolerate read-only config during merge --- README.md | 21 +++++++++++++++------ docker-compose.yml | 4 ++-- docker-entrypoint.sh | 35 ++++++++++++++++++++++++++++++++--- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 3d94089..6c6162f 100644 --- a/README.md +++ b/README.md @@ -349,23 +349,32 @@ You can now run pyMC Repeater from within a [Docker Container](https://www.docke Here is what you'll need to do in order to get the container running: -1. Copy the `config.yaml.example` to `config.yaml` +1. Create the bind mount directories and copy the example config into the + Docker config directory. ```bash -cp ./config.yaml.example ./config.yaml +mkdir -p ./config ./data +cp ./config.yaml.example ./config/config.yaml ``` 2. Run the configuration script and follow the prompts. ```bash -sudo bash ./setup-radio-config.sh +sudo bash ./setup-radio-config.sh ./config ``` -3. Modify the `config.yaml` file with a unique web UI password. This allows you to bypass the `/setup` page when logging for the first time. You can find the value under `repeater.security.admin_password`. Change to _anything_ besides the default of `admin123`. +3. Modify `./config/config.yaml` with a unique web UI password. This allows you to bypass the `/setup` page when logging for the first time. You can find the value under `repeater.security.admin_password`. Change to _anything_ besides the default of `admin123`. 4. Configure the [docker compose](./docker-compose.yml) to your specific hardware and file paths. Be sure to comment-out or delete lines that aren't required for your hardware. Please note that your hardware devices might be at a different path than those listed in the docker compose file. -5. If you are using SPI/GPIO hardware, make sure the `GPIO_GID` and `SPI_GID` +5. Make the bind mount directories writable by the container user. The image + runs as UID/GID `15888` by default unless you override `PUID` and `PGID`. + +```bash +sudo chown -R 15888:15888 ./config ./data +``` + +6. If you are using SPI/GPIO hardware, make sure the `GPIO_GID` and `SPI_GID` values match the numeric group IDs on your host. The compose file defaults to `GPIO_GID=986` and `SPI_GID=989`. @@ -374,7 +383,7 @@ getent group gpio getent group spi ``` -6. Build and start the container. +7. Build and start the container. ```bash docker compose up -d --force-recreate --build diff --git a/docker-compose.yml b/docker-compose.yml index 98a862d..e65766b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,8 +4,8 @@ services: context: . dockerfile: dockerfile args: - PUID: ${PUID:-1000} - PGID: ${PGID:-1000} + PUID: ${PUID:-15888} + PGID: ${PGID:-15888} GPIO_GID: ${GPIO_GID:-986} SPI_GID: ${SPI_GID:-989} container_name: pymc-repeater diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 16788ac..518b1da 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -13,16 +13,38 @@ YQ_CMD="${YQ_CMD:-/usr/local/bin/yq}" mkdir -p "${CONFIG_DIR}" +print_permission_help() { + echo "If you are bind-mounting ./config or ./data, ensure the host paths are writable by ${RUNTIME_USER} (${RUNTIME_UID}:${RUNTIME_GID})." >&2 + echo "For the default image user, run: sudo chown -R ${RUNTIME_UID}:${RUNTIME_GID} ./config ./data" >&2 +} + copy_or_die() { src="$1" dest="$2" if ! cp "${src}" "${dest}"; then echo "Failed to initialize ${dest} from ${src}." >&2 - echo "If you are bind-mounting ./config.yaml, ensure the host path is writable by ${RUNTIME_USER} (${RUNTIME_UID}:${RUNTIME_GID})." >&2 + print_permission_help exit 1 fi } +use_runtime_merged_config() { + src="$1" + runtime_dir="$(mktemp -d /tmp/pymc-repeater-config.XXXXXX)" + runtime_config="${runtime_dir}/config.yaml" + + if ! cp "${src}" "${runtime_config}"; then + echo "Failed to prepare temporary merged config at ${runtime_config}; keeping the existing config." >&2 + return 1 + fi + + CONFIG_PATH="${runtime_config}" + echo "Using merged config from ${CONFIG_PATH} for this container start only." >&2 + echo "Fix the bind-mounted config ownership so future upgrades can persist merged config changes." >&2 + print_permission_help + return 0 +} + merge_config_from_example() { config_path="$1" @@ -62,7 +84,10 @@ merge_config_from_example() { fi if ! cmp -s "${config_path}" "${merged_config}"; then - copy_or_die "${merged_config}" "${config_path}" + if ! cp "${merged_config}" "${config_path}"; then + echo "Failed to update ${config_path} from merged config; the bind-mounted config is not writable." >&2 + use_runtime_merged_config "${merged_config}" || true + fi fi cleanup_merge @@ -70,7 +95,11 @@ merge_config_from_example() { } if [ ! -f "${EXAMPLE_PATH}" ] && [ -f "${BUNDLED_EXAMPLE_PATH}" ]; then - copy_or_die "${BUNDLED_EXAMPLE_PATH}" "${EXAMPLE_PATH}" + if ! cp "${BUNDLED_EXAMPLE_PATH}" "${EXAMPLE_PATH}"; then + echo "Could not copy bundled example config to ${EXAMPLE_PATH}; using bundled example for config merge only." >&2 + print_permission_help + EXAMPLE_PATH="${BUNDLED_EXAMPLE_PATH}" + fi fi if [ -d "${CONFIG_PATH}" ]; then From b5df705b8714d65e30a9834520742b34b302da00 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Thu, 28 May 2026 14:40:23 -0400 Subject: [PATCH 3/9] docs: clarify docker setup config steps --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6c6162f..91c4b58 100644 --- a/README.md +++ b/README.md @@ -363,7 +363,9 @@ cp ./config.yaml.example ./config/config.yaml sudo bash ./setup-radio-config.sh ./config ``` -3. Modify `./config/config.yaml` with a unique web UI password. This allows you to bypass the `/setup` page when logging for the first time. You can find the value under `repeater.security.admin_password`. Change to _anything_ besides the default of `admin123`. +3. Review `./config/config.yaml` before first start. You can preconfigure the + radio, location, and web UI password there, or leave first-run setup to the + web interface. 4. Configure the [docker compose](./docker-compose.yml) to your specific hardware and file paths. Be sure to comment-out or delete lines that aren't required for your hardware. Please note that your hardware devices might be at a different path than those listed in the docker compose file. @@ -375,8 +377,8 @@ sudo chown -R 15888:15888 ./config ./data ``` 6. If you are using SPI/GPIO hardware, make sure the `GPIO_GID` and `SPI_GID` - values match the numeric group IDs on your host. The compose file defaults - to `GPIO_GID=986` and `SPI_GID=989`. + values match the numeric group IDs on your host. These IDs can vary by OS + image, so check the host before starting the container. ```bash getent group gpio From 7015e0eb15844817c448829e93cbddd9f0b9dee4 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Thu, 28 May 2026 14:46:17 -0400 Subject: [PATCH 4/9] docs: document docker gpio gids --- README.md | 30 +++++++++++++++++++++++++----- docker-compose.yml | 19 +++++++++++-------- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 91c4b58..9c028b2 100644 --- a/README.md +++ b/README.md @@ -367,10 +367,10 @@ sudo bash ./setup-radio-config.sh ./config radio, location, and web UI password there, or leave first-run setup to the web interface. -4. Configure the [docker compose](./docker-compose.yml) to your specific hardware and file paths. Be sure to comment-out or delete lines that aren't required for your hardware. Please note that your hardware devices might be at a different path than those listed in the docker compose file. +4. Configure the [docker compose](./docker-compose.yml) to your specific hardware and file paths. Be sure to comment-out or delete lines that aren't required for your hardware. Please note that your hardware devices might be at a different path than those listed in the docker compose file. By default, the compose file pulls the published `pymcdev/pymc-repeater:dev` image. If you need a different image tag or repository, set `PYMC_REPEATER_IMAGE` in a `.env` file. 5. Make the bind mount directories writable by the container user. The image - runs as UID/GID `15888` by default unless you override `PUID` and `PGID`. + runs as UID/GID `15888` by default. ```bash sudo chown -R 15888:15888 ./config ./data @@ -378,19 +378,39 @@ sudo chown -R 15888:15888 ./config ./data 6. If you are using SPI/GPIO hardware, make sure the `GPIO_GID` and `SPI_GID` values match the numeric group IDs on your host. These IDs can vary by OS - image, so check the host before starting the container. + image, so check the host before starting the container. If the values do + not match your host, put the correct numeric IDs in `.env`. ```bash getent group gpio getent group spi ``` -7. Build and start the container. +Example output: + +```text +gpio:x:997: +spi:x:999: +``` + +Example `.env`: ```bash -docker compose up -d --force-recreate --build +GPIO_GID=997 +SPI_GID=999 ``` +7. Pull and start the container. + +```bash +docker compose pull +docker compose up -d +``` + +If you are developing locally and want Docker Compose to build the image from +this checkout instead, uncomment the `build:` block in `docker-compose.yml` and +run `docker compose up -d --build`. + ## Roadmap / Planned Features - [ ] **Public Map Integration** - Submit repeater location and details to public map for discovery diff --git a/docker-compose.yml b/docker-compose.yml index e65766b..78633bd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,16 @@ services: pymc-repeater: - build: - context: . - dockerfile: dockerfile - args: - PUID: ${PUID:-15888} - PGID: ${PGID:-15888} - GPIO_GID: ${GPIO_GID:-986} - SPI_GID: ${SPI_GID:-989} + image: ${PYMC_REPEATER_IMAGE:-pymcdev/pymc-repeater:dev} + # Uncomment this block to build from a local checkout instead of pulling + # the published Docker image. + # build: + # context: . + # dockerfile: dockerfile + # args: + # PUID: ${PUID:-15888} + # PGID: ${PGID:-15888} + # GPIO_GID: ${GPIO_GID:-986} + # SPI_GID: ${SPI_GID:-989} container_name: pymc-repeater restart: unless-stopped ports: From d333deb1e57f32c7b69261f638eb222030b5a643 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Fri, 29 May 2026 12:26:26 -0400 Subject: [PATCH 5/9] docker: clarify compose env setup --- .env.example | 22 ++++++++++++++++++++++ .gitignore | 1 + README.md | 33 +++++++++++++++++++++++---------- docker-compose.build.yml | 10 ++++++++++ docker-compose.yml | 12 +----------- docker-entrypoint.sh | 14 ++++++++++++++ 6 files changed, 71 insertions(+), 21 deletions(-) create mode 100644 .env.example create mode 100644 docker-compose.build.yml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..12fde7d --- /dev/null +++ b/.env.example @@ -0,0 +1,22 @@ +# Copy this file to .env before running Docker Compose: +# cp .env.example .env + +# Published image to run. Use a different repository or tag if you are testing +# a fork or a specific release. +PYMC_REPEATER_IMAGE=pymcdev/pymc-repeater:dev +# PYMC_REPEATER_IMAGE=yellowcooln/pymc-repeater:dev + +# SPI/GPIO access uses the host's numeric group IDs. Check your host with: +# getent group gpio +# getent group spi +# Example output: +# gpio:x:997: +# spi:x:999: +# Put the third field from each output line below. +GPIO_GID=986 +SPI_GID=989 + +# Local build only. These are used by docker-compose.build.yml if you build the +# image yourself instead of pulling PYMC_REPEATER_IMAGE. +PUID=15888 +PGID=15888 diff --git a/.gitignore b/.gitignore index 03a1ce0..3942f65 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ htmlcov/ *~ # Config +.env config.yaml config.yaml.backup identity.json diff --git a/README.md b/README.md index 9c028b2..69e1669 100644 --- a/README.md +++ b/README.md @@ -349,7 +349,13 @@ You can now run pyMC Repeater from within a [Docker Container](https://www.docke Here is what you'll need to do in order to get the container running: -1. Create the bind mount directories and copy the example config into the +1. Copy the Docker environment example and adjust it for your host if needed. + +```bash +cp .env.example .env +``` + +2. Create the bind mount directories and copy the example config into the Docker config directory. ```bash @@ -357,26 +363,30 @@ mkdir -p ./config ./data cp ./config.yaml.example ./config/config.yaml ``` -2. Run the configuration script and follow the prompts. +Do not bind mount `./config.yaml` directly. The supported Docker layout mounts +the whole `./config` directory to `/etc/pymc_repeater`, with the config file at +`./config/config.yaml`. + +3. Run the configuration script and follow the prompts. ```bash sudo bash ./setup-radio-config.sh ./config ``` -3. Review `./config/config.yaml` before first start. You can preconfigure the +4. Review `./config/config.yaml` before first start. You can preconfigure the radio, location, and web UI password there, or leave first-run setup to the web interface. -4. Configure the [docker compose](./docker-compose.yml) to your specific hardware and file paths. Be sure to comment-out or delete lines that aren't required for your hardware. Please note that your hardware devices might be at a different path than those listed in the docker compose file. By default, the compose file pulls the published `pymcdev/pymc-repeater:dev` image. If you need a different image tag or repository, set `PYMC_REPEATER_IMAGE` in a `.env` file. +5. Configure the [docker compose](./docker-compose.yml) to your specific hardware and file paths. Be sure to comment-out or delete lines that aren't required for your hardware. Please note that your hardware devices might be at a different path than those listed in the docker compose file. By default, the compose file pulls the published `pymcdev/pymc-repeater:dev` image. If you need a different image tag or repository, set `PYMC_REPEATER_IMAGE` in `.env`. -5. Make the bind mount directories writable by the container user. The image +6. Make the bind mount directories writable by the container user. The image runs as UID/GID `15888` by default. ```bash sudo chown -R 15888:15888 ./config ./data ``` -6. If you are using SPI/GPIO hardware, make sure the `GPIO_GID` and `SPI_GID` +7. If you are using SPI/GPIO hardware, make sure the `GPIO_GID` and `SPI_GID` values match the numeric group IDs on your host. These IDs can vary by OS image, so check the host before starting the container. If the values do not match your host, put the correct numeric IDs in `.env`. @@ -393,14 +403,14 @@ gpio:x:997: spi:x:999: ``` -Example `.env`: +Example `.env` values: ```bash GPIO_GID=997 SPI_GID=999 ``` -7. Pull and start the container. +8. Pull and start the container. ```bash docker compose pull @@ -408,8 +418,11 @@ docker compose up -d ``` If you are developing locally and want Docker Compose to build the image from -this checkout instead, uncomment the `build:` block in `docker-compose.yml` and -run `docker compose up -d --build`. +this checkout instead, use the local build override: + +```bash +docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build +``` ## Roadmap / Planned Features diff --git a/docker-compose.build.yml b/docker-compose.build.yml new file mode 100644 index 0000000..8933b8d --- /dev/null +++ b/docker-compose.build.yml @@ -0,0 +1,10 @@ +services: + pymc-repeater: + build: + context: . + dockerfile: dockerfile + args: + PUID: ${PUID:-15888} + PGID: ${PGID:-15888} + GPIO_GID: ${GPIO_GID:-986} + SPI_GID: ${SPI_GID:-989} diff --git a/docker-compose.yml b/docker-compose.yml index 78633bd..eda6d96 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,16 +1,6 @@ services: pymc-repeater: image: ${PYMC_REPEATER_IMAGE:-pymcdev/pymc-repeater:dev} - # Uncomment this block to build from a local checkout instead of pulling - # the published Docker image. - # build: - # context: . - # dockerfile: dockerfile - # args: - # PUID: ${PUID:-15888} - # PGID: ${PGID:-15888} - # GPIO_GID: ${GPIO_GID:-986} - # SPI_GID: ${SPI_GID:-989} container_name: pymc-repeater restart: unless-stopped ports: @@ -24,7 +14,7 @@ services: # SPI DEVICES PERMISSIONS cap_add: - SYS_RAWIO - # USB DEVICSE PERMISSIONS + # USB DEVICE PERMISSIONS group_add: - "${GPIO_GID:-986}" - "${SPI_GID:-989}" diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 518b1da..eb17104 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -18,6 +18,16 @@ print_permission_help() { echo "For the default image user, run: sudo chown -R ${RUNTIME_UID}:${RUNTIME_GID} ./config ./data" >&2 } +fail_bad_config_mount() { + echo "Invalid Docker config mount: ${CONFIG_PATH} is a directory, but it must be the config file." >&2 + echo "This usually happens when ./config.yaml is bind-mounted before that host file exists." >&2 + echo "Use the supported folder mount instead:" >&2 + echo " - ./config:/etc/pymc_repeater" >&2 + echo "Then place the config at ./config/config.yaml." >&2 + print_permission_help + exit 1 +} + copy_or_die() { src="$1" dest="$2" @@ -94,6 +104,10 @@ merge_config_from_example() { trap - EXIT HUP INT TERM } +if [ -d "${CONFIG_PATH}" ] && [ "$(basename "${CONFIG_PATH}")" = "config.yaml" ]; then + fail_bad_config_mount +fi + if [ ! -f "${EXAMPLE_PATH}" ] && [ -f "${BUNDLED_EXAMPLE_PATH}" ]; then if ! cp "${BUNDLED_EXAMPLE_PATH}" "${EXAMPLE_PATH}"; then echo "Could not copy bundled example config to ${EXAMPLE_PATH}; using bundled example for config merge only." >&2 From ca50656560b3ca04d24e414363345433a77d9c5e Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Fri, 29 May 2026 12:29:51 -0400 Subject: [PATCH 6/9] docs: remove fork image from env example --- .env.example | 1 - 1 file changed, 1 deletion(-) diff --git a/.env.example b/.env.example index 12fde7d..da1c6cf 100644 --- a/.env.example +++ b/.env.example @@ -4,7 +4,6 @@ # Published image to run. Use a different repository or tag if you are testing # a fork or a specific release. PYMC_REPEATER_IMAGE=pymcdev/pymc-repeater:dev -# PYMC_REPEATER_IMAGE=yellowcooln/pymc-repeater:dev # SPI/GPIO access uses the host's numeric group IDs. Check your host with: # getent group gpio From 77480c6c1c5a27b848b27c7f008e144a32b19626 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Fri, 29 May 2026 16:14:03 -0400 Subject: [PATCH 7/9] docker: use named volumes by default --- .env.example | 10 ++++++ README.md | 72 +++++++++++++++++++++++----------------- docker-compose.build.yml | 1 + docker-compose.yml | 8 +++-- 4 files changed, 59 insertions(+), 32 deletions(-) diff --git a/.env.example b/.env.example index da1c6cf..b866706 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,16 @@ # a fork or a specific release. PYMC_REPEATER_IMAGE=pymcdev/pymc-repeater:dev +# Storage defaults to Docker named volumes. This is the safest option for +# Portainer and fresh installs because Docker preserves the image ownership. +# To use host bind mounts instead, create the folders first and make them +# writable by UID/GID 15888: +# sudo mkdir -p /opt/pymc-repeater/config /opt/pymc-repeater/data +# sudo chown -R 15888:15888 /opt/pymc-repeater/config /opt/pymc-repeater/data +# Then uncomment and adjust these paths: +# PYMC_CONFIG_VOLUME=/opt/pymc-repeater/config +# PYMC_DATA_VOLUME=/opt/pymc-repeater/data + # SPI/GPIO access uses the host's numeric group IDs. Check your host with: # getent group gpio # getent group spi diff --git a/README.md b/README.md index 69e1669..4109865 100644 --- a/README.md +++ b/README.md @@ -355,38 +355,21 @@ Here is what you'll need to do in order to get the container running: cp .env.example .env ``` -2. Create the bind mount directories and copy the example config into the - Docker config directory. +2. Configure the [docker compose](./docker-compose.yml) to your specific + hardware and file paths. Be sure to comment-out or delete lines that aren't + required for your hardware. Please note that your hardware devices might be + at a different path than those listed in the docker compose file. -```bash -mkdir -p ./config ./data -cp ./config.yaml.example ./config/config.yaml -``` +By default, the compose file pulls the published `pymcdev/pymc-repeater:dev` +image and stores config/data in Docker named volumes. This is the recommended +default for Portainer and fresh installs because Docker keeps the volume +ownership compatible with the container user. -Do not bind mount `./config.yaml` directly. The supported Docker layout mounts -the whole `./config` directory to `/etc/pymc_repeater`, with the config file at -`./config/config.yaml`. +Do not bind mount `./config.yaml` directly. If you use a bind mount, mount a +config directory to `/etc/pymc_repeater`, with the config file at +`config.yaml` inside that directory. -3. Run the configuration script and follow the prompts. - -```bash -sudo bash ./setup-radio-config.sh ./config -``` - -4. Review `./config/config.yaml` before first start. You can preconfigure the - radio, location, and web UI password there, or leave first-run setup to the - web interface. - -5. Configure the [docker compose](./docker-compose.yml) to your specific hardware and file paths. Be sure to comment-out or delete lines that aren't required for your hardware. Please note that your hardware devices might be at a different path than those listed in the docker compose file. By default, the compose file pulls the published `pymcdev/pymc-repeater:dev` image. If you need a different image tag or repository, set `PYMC_REPEATER_IMAGE` in `.env`. - -6. Make the bind mount directories writable by the container user. The image - runs as UID/GID `15888` by default. - -```bash -sudo chown -R 15888:15888 ./config ./data -``` - -7. If you are using SPI/GPIO hardware, make sure the `GPIO_GID` and `SPI_GID` +3. If you are using SPI/GPIO hardware, make sure the `GPIO_GID` and `SPI_GID` values match the numeric group IDs on your host. These IDs can vary by OS image, so check the host before starting the container. If the values do not match your host, put the correct numeric IDs in `.env`. @@ -410,13 +393,42 @@ GPIO_GID=997 SPI_GID=999 ``` -8. Pull and start the container. +4. Pull and start the container. ```bash docker compose pull docker compose up -d ``` +### Optional host bind mounts + +If you want config and data stored in normal host folders instead of Docker +named volumes, create absolute host paths first and make them writable by the +container user. The image runs as UID/GID `15888` by default. + +```bash +sudo mkdir -p /opt/pymc-repeater/config /opt/pymc-repeater/data +sudo chown -R 15888:15888 /opt/pymc-repeater/config /opt/pymc-repeater/data +``` + +Then set the bind mount paths in `.env`: + +```bash +PYMC_CONFIG_VOLUME=/opt/pymc-repeater/config +PYMC_DATA_VOLUME=/opt/pymc-repeater/data +``` + +You can preconfigure the file before first start: + +```bash +cp ./config.yaml.example /opt/pymc-repeater/config/config.yaml +sudo bash ./setup-radio-config.sh /opt/pymc-repeater/config +sudo chown -R 15888:15888 /opt/pymc-repeater/config /opt/pymc-repeater/data +``` + +If you skip this, the container will create `config.yaml` from the bundled +example on first start. + If you are developing locally and want Docker Compose to build the image from this checkout instead, use the local build override: diff --git a/docker-compose.build.yml b/docker-compose.build.yml index 8933b8d..0f1c2f6 100644 --- a/docker-compose.build.yml +++ b/docker-compose.build.yml @@ -1,5 +1,6 @@ services: pymc-repeater: + image: pymc-repeater:local build: context: . dockerfile: dockerfile diff --git a/docker-compose.yml b/docker-compose.yml index eda6d96..601223e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,5 +20,9 @@ services: - "${SPI_GID:-989}" - plugdev volumes: - - ./config:/etc/pymc_repeater - - ./data:/var/lib/pymc_repeater + - ${PYMC_CONFIG_VOLUME:-pymc-repeater-config}:/etc/pymc_repeater + - ${PYMC_DATA_VOLUME:-pymc-repeater-data}:/var/lib/pymc_repeater + +volumes: + pymc-repeater-config: + pymc-repeater-data: From 1569b11690451bca4b122fe370f099e3e090459b Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Mon, 8 Jun 2026 20:27:48 -0400 Subject: [PATCH 8/9] docker: default compose image to main --- .env.example | 2 +- README.md | 2 +- docker-compose.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index b866706..af3af2c 100644 --- a/.env.example +++ b/.env.example @@ -3,7 +3,7 @@ # Published image to run. Use a different repository or tag if you are testing # a fork or a specific release. -PYMC_REPEATER_IMAGE=pymcdev/pymc-repeater:dev +PYMC_REPEATER_IMAGE=pymcdev/pymc-repeater:main # Storage defaults to Docker named volumes. This is the safest option for # Portainer and fresh installs because Docker preserves the image ownership. diff --git a/README.md b/README.md index 4109865..1fbb19f 100644 --- a/README.md +++ b/README.md @@ -360,7 +360,7 @@ cp .env.example .env required for your hardware. Please note that your hardware devices might be at a different path than those listed in the docker compose file. -By default, the compose file pulls the published `pymcdev/pymc-repeater:dev` +By default, the compose file pulls the published `pymcdev/pymc-repeater:main` image and stores config/data in Docker named volumes. This is the recommended default for Portainer and fresh installs because Docker keeps the volume ownership compatible with the container user. diff --git a/docker-compose.yml b/docker-compose.yml index 601223e..d2aff30 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: pymc-repeater: - image: ${PYMC_REPEATER_IMAGE:-pymcdev/pymc-repeater:dev} + image: ${PYMC_REPEATER_IMAGE:-pymcdev/pymc-repeater:main} container_name: pymc-repeater restart: unless-stopped ports: From d5001a235d235d1255ff8d6a68578d312b60ef26 Mon Sep 17 00:00:00 2001 From: Yellowcooln <12516003+yellowcooln@users.noreply.github.com> Date: Mon, 8 Jun 2026 20:31:32 -0400 Subject: [PATCH 9/9] docs: remove docker readme edits --- README.md | 81 ++++++------------------------------------------------- 1 file changed, 8 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 1fbb19f..d77e60b 100644 --- a/README.md +++ b/README.md @@ -349,91 +349,26 @@ You can now run pyMC Repeater from within a [Docker Container](https://www.docke Here is what you'll need to do in order to get the container running: -1. Copy the Docker environment example and adjust it for your host if needed. +1. Copy the `config.yaml.example` to `config.yaml` ```bash -cp .env.example .env +cp ./config.yaml.example ./config.yaml ``` -2. Configure the [docker compose](./docker-compose.yml) to your specific - hardware and file paths. Be sure to comment-out or delete lines that aren't - required for your hardware. Please note that your hardware devices might be - at a different path than those listed in the docker compose file. - -By default, the compose file pulls the published `pymcdev/pymc-repeater:main` -image and stores config/data in Docker named volumes. This is the recommended -default for Portainer and fresh installs because Docker keeps the volume -ownership compatible with the container user. - -Do not bind mount `./config.yaml` directly. If you use a bind mount, mount a -config directory to `/etc/pymc_repeater`, with the config file at -`config.yaml` inside that directory. - -3. If you are using SPI/GPIO hardware, make sure the `GPIO_GID` and `SPI_GID` - values match the numeric group IDs on your host. These IDs can vary by OS - image, so check the host before starting the container. If the values do - not match your host, put the correct numeric IDs in `.env`. +2. Run the configuration script and follow the prompts. ```bash -getent group gpio -getent group spi +sudo bash ./setup-radio-config.sh ``` -Example output: +3. Modify the `config.yaml` file with a unique web UI password. This allows you to bypass the `/setup` page when logging for the first time. You can find the value under `repeater.security.admin_password`. Change to _anything_ besides the default of `admin123`. -```text -gpio:x:997: -spi:x:999: -``` +4. Configure the [docker compose](./docker-compose.yml) to your specific hardware and file paths. Be sure to comment-out or delete lines that aren't required for your hardware. Please note that your hardware devices might be at a different path than those listed in the docker compose file. -Example `.env` values: +5. Build and start the container. ```bash -GPIO_GID=997 -SPI_GID=999 -``` - -4. Pull and start the container. - -```bash -docker compose pull -docker compose up -d -``` - -### Optional host bind mounts - -If you want config and data stored in normal host folders instead of Docker -named volumes, create absolute host paths first and make them writable by the -container user. The image runs as UID/GID `15888` by default. - -```bash -sudo mkdir -p /opt/pymc-repeater/config /opt/pymc-repeater/data -sudo chown -R 15888:15888 /opt/pymc-repeater/config /opt/pymc-repeater/data -``` - -Then set the bind mount paths in `.env`: - -```bash -PYMC_CONFIG_VOLUME=/opt/pymc-repeater/config -PYMC_DATA_VOLUME=/opt/pymc-repeater/data -``` - -You can preconfigure the file before first start: - -```bash -cp ./config.yaml.example /opt/pymc-repeater/config/config.yaml -sudo bash ./setup-radio-config.sh /opt/pymc-repeater/config -sudo chown -R 15888:15888 /opt/pymc-repeater/config /opt/pymc-repeater/data -``` - -If you skip this, the container will create `config.yaml` from the bundled -example on first start. - -If you are developing locally and want Docker Compose to build the image from -this checkout instead, use the local build override: - -```bash -docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build +docker compose up -d --force-recreate --build ``` ## Roadmap / Planned Features