Problem
There is a working Dockerfile, and .github/workflows/deploy-server.yml builds an image on every push to main — but it pushes to a private Artifact Registry behind Workload Identity Federation, gated on four repo secrets (GCP_PROJECT_ID, GCP_REGION, GCP_SERVICE_ACCOUNT, GCP_WORKLOAD_IDENTITY_PROVIDER). That's the maintainer's deployment pipeline, not a distribution channel.
So there is no image anyone else can pull. To try StreamCore you must clone the repo, install Go, and build — even though the README's own quick start is otherwise a copy-paste affair and a docker-compose.yml exists at the root of the parent project. For a media server whose runtime already needs Node and Python present for plugins, "docker run and go" is a materially lower barrier than "get a Go toolchain".
The image is also linux/amd64 only. A large share of the likely audience is developing on Apple Silicon, where an amd64 image runs under emulation — for a latency-sensitive audio path that's not a reasonable way to evaluate the project.
Proposed change
Add .github/workflows/publish-image.yml publishing to GHCR (ghcr.io/streamcoreai/streamcore-server). GHCR needs no external account setup — GITHUB_TOKEN with packages: write is sufficient, which keeps this independent of the GCP credentials the deploy workflow uses.
- Trigger on release/tag publication, and on push to
main for a :main floating tag. Do not touch deploy-server.yml — the deployment path should keep working exactly as it does.
- Build
linux/amd64 and linux/arm64 via docker/setup-qemu-action + docker/setup-buildx-action, which docker/build-push-action@v6 (already in use in the deploy workflow) handles natively.
- Tag with
docker/metadata-action: semver on tags, main, and the commit SHA.
- Enable GitHub Actions cache (
cache-from: type=gha) — the runtime stage does npm install -g tsx, creates a Python venv, and loops npm install over every plugin directory. Uncached, that's slow; on emulated arm64 it's very slow. Expect the first arm64 build to be the long pole and check the wall time before merging.
- Add a "Run with Docker" section to the README quick start with the pull command and the config mount.
The build stage is already CGO_ENABLED=0 (the Opus codec runs as WebAssembly through wazero, per the Dockerfile comment), so the Go side cross-compiles cleanly. The runtime stage is node:22-bookworm-slim, which publishes arm64 — no base image change needed.
Worth confirming
Whether the plugin pip install / npm install steps in the runtime stage succeed under arm64 emulation. If some plugin dependency has no arm64 wheel, note it on the issue — that's useful information regardless of how this lands.
Acceptance criteria
Pointers
Dockerfile — two-stage build; note the port exposures, including the TURN relay range 50001-60000/udp, which the README run instructions must account for
.github/workflows/deploy-server.yml — existing build-push-action usage to mirror
Problem
There is a working
Dockerfile, and.github/workflows/deploy-server.ymlbuilds an image on every push tomain— but it pushes to a private Artifact Registry behind Workload Identity Federation, gated on four repo secrets (GCP_PROJECT_ID,GCP_REGION,GCP_SERVICE_ACCOUNT,GCP_WORKLOAD_IDENTITY_PROVIDER). That's the maintainer's deployment pipeline, not a distribution channel.So there is no image anyone else can pull. To try StreamCore you must clone the repo, install Go, and build — even though the README's own quick start is otherwise a copy-paste affair and a
docker-compose.ymlexists at the root of the parent project. For a media server whose runtime already needs Node and Python present for plugins, "docker run and go" is a materially lower barrier than "get a Go toolchain".The image is also
linux/amd64only. A large share of the likely audience is developing on Apple Silicon, where an amd64 image runs under emulation — for a latency-sensitive audio path that's not a reasonable way to evaluate the project.Proposed change
Add
.github/workflows/publish-image.ymlpublishing to GHCR (ghcr.io/streamcoreai/streamcore-server). GHCR needs no external account setup —GITHUB_TOKENwithpackages: writeis sufficient, which keeps this independent of the GCP credentials the deploy workflow uses.mainfor a:mainfloating tag. Do not touchdeploy-server.yml— the deployment path should keep working exactly as it does.linux/amd64andlinux/arm64viadocker/setup-qemu-action+docker/setup-buildx-action, whichdocker/build-push-action@v6(already in use in the deploy workflow) handles natively.docker/metadata-action: semver on tags,main, and the commit SHA.cache-from: type=gha) — the runtime stage doesnpm install -g tsx, creates a Python venv, and loopsnpm installover every plugin directory. Uncached, that's slow; on emulated arm64 it's very slow. Expect the first arm64 build to be the long pole and check the wall time before merging.The build stage is already
CGO_ENABLED=0(the Opus codec runs as WebAssembly through wazero, per the Dockerfile comment), so the Go side cross-compiles cleanly. The runtime stage isnode:22-bookworm-slim, which publishes arm64 — no base image change needed.Worth confirming
Whether the plugin
pip install/npm installsteps in the runtime stage succeed under arm64 emulation. If some plugin dependency has no arm64 wheel, note it on the issue — that's useful information regardless of how this lands.Acceptance criteria
docker pull ghcr.io/streamcoreai/streamcore-server:mainworks for an anonymous user.docker manifest inspectshows bothlinux/amd64andlinux/arm64./health.config.toml.deploy-server.ymlis unchanged and still green.Pointers
Dockerfile— two-stage build; note the port exposures, including the TURN relay range50001-60000/udp, which the README run instructions must account for.github/workflows/deploy-server.yml— existingbuild-push-actionusage to mirror