Upscayl Docker Worker
A private, authenticated, GPU-backed Upscayl worker with a local Hermes MCPbridge. The worker runs the standalone NCNN/Vulkan backend in Docker; Hermesruns the bridge locally so private image files do not pass through modelcontext.
Hermes -> local stdio MCP bridge -> authenticated worker API -> NVIDIA/Vulkan
<- validated local PNG <- validated job result <- Upscayl NCNN
This repository intentionally contains both halves:
upscayl_worker/— the FastAPI service packaged in the Docker image.upscayl_bridge/— the local stdio MCP bridge installed on the Hermes host.
A separate MCP repository is not needed. The bridge and worker share one APIcontract, test suite, and release lifecycle, while still running in differentplaces. Splitting them would add version coordination without improving thesecurity boundary.
Published image
The production image is published at:
yellowcooln/upscayl-docker-worker
Supported platform: linux/amd64.
GitHub Actions publishes:
latestfrom the default branch- branch tags such as
mainanddev - version tags such as
v1.0.0 - source-reference tags such as
sha-0883691
Registry tags, including sha-*, can be overwritten. Only a registry digest isan immutable deployment reference.
The image is built from the official Upscayl Flatpak on a hosted runner. Thebackend and models remain absent from Git, while their Flatpak version andcommit are recorded inside the image at:
/usr/share/doc/upscayl-docker-worker/upscayl-source.txt
What is included
- Pre-parser bearer authentication and streamed upload limits
- Decoded pixel, dimension, output, intermediate, timeout, queue, and disk limits
- One active GPU worker with bounded pending admission
- Automatic photo/digital routing and exact 1×–16× output planning
- Expiring opaque job storage and restart orphan cleanup
- A narrow MCP surface:
upscale_image,list_models, andworker_status - Real API, MCP, PNG/CRC, and GPU-utilization verification scripts
Worker prerequisites
- Linux x86-64 Docker host with an NVIDIA GPU
- NVIDIA driver and NVIDIA Container Toolkit
- Docker Engine; Docker Compose is optional
- Python 3 for fixture generation and independent PNG validation
Confirm basic GPU visibility:
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 \
nvidia-smi --query-gpu=name,driver_version --format=csv,noheader
nvidia-smi alone is not proof that Upscayl works. NCNN also requires afunctional Vulkan ICD inside the container.
Deploy the published image
Clone the repository for the hardened deployment scripts and configuration:
git clone https://github.com/yellowcooln/upscayl-docker-worker.git
cd upscayl-docker-worker
cp .env.example .env
chmod 600 .env
Set a random UPSCAYL_WORKER_TOKEN, the correct NVIDIA driver version, and theprivate bind address in .env. The worker and deployment script reject thepublic placeholder token from .env.example. Then deploy and verify:
scripts/deploy-api.sh
scripts/verify-api.sh
deploy-api.sh pulls yellowcooln/upscayl-docker-worker:latest by default,validates that it is linux/amd64, and only then recreates the service. Itprints the deployed image ID and repository digest.
Docker Compose provides the same image-first deployment:
docker compose up -d
For reproducible production deployment, set UPSCAYL_IMAGE to a version tag orimmutable registry digest instead of latest:
UPSCAYL_IMAGE=yellowcooln/upscayl-docker-worker@sha256:<digest>
UPSCAYL_PULL_POLICY=always
The API binds to loopback by default. Plain HTTP is appropriate only onloopback or an encrypted trusted network such as a VPN; use HTTPS for any otherpath and never publish the worker directly to the Internet.
Verify direct GPU inference
The direct backend verifier now uses the published image rather than rebuildinglocally:
scripts/verify-gpu.sh
It generates a deterministic 512×384 fixture, invokes the container'supscayl-bin entrypoint directly, validates the 2048×1536 output structure andCRCs, and prints hashes.
For a manual invocation, place an image under io/ and run:
scripts/run-worker.sh \
-i /work/input.png \
-o /work/output.png \
-m /opt/upscayl/models \
-n high-fidelity-4x \
-z 4 \
-s 4 \
-f png \
-v
Hermes MCP bridge
The bridge must run on the Hermes host because that is where local attachmentsand filesystem paths exist. It should not run inside the remote GPU container.Install it from this same checkout:
uv sync --frozen --no-dev --extra bridge
The bridge uses these environment-backed settings:
UPSCAYL_WORKER_URL=http://private-gpu-host:8788
UPSCAYL_WORKER_TOKEN=<secret>
UPSCAYL_ALLOW_INSECURE_HTTP=true
UPSCAYL_ALLOWED_INPUT_ROOTS=/home/user/.hermes:/home/user/Pictures
UPSCAYL_OUTPUT_DIR=/home/user/Pictures/upscayl
Non-loopback HTTP requires the explicit UPSCAYL_ALLOW_INSECURE_HTTP=trueacknowledgement; omit it when using HTTPS or loopback. UPSCAYL_ALLOWED_INPUT_ROOTSis required; the bridge fails closed instead ofdefaulting to the whole home directory. Store these settings in the activeHermes profile's mode-0600 .env. Register the bridge without copying the tokeninto config.yaml or process arguments:
hermes mcp add upscayl \
--command /usr/bin/bash \
--connect-timeout 60 \
--args -lc 'set -a; source "${HERMES_HOME:-$HOME/.hermes}/.env"; set +a; exec /absolute/path/to/repo/.venv/bin/upscayl-mcp'
Enable only upscale_image, list_models, and worker_status. Then verify bothdiscovery and a real GPU job:
hermes mcp test upscayl
python3 scripts/create_fixture.py io/upscayl-mcp-fixture.png
uv run python scripts/verify-mcp.py io/upscayl-mcp-fixture.png
Run /reload-mcp or start a new Hermes session after changing the MCP setup.Image bytes travel directly between the local bridge and authenticated worker;they never enter model context.
Maintainer/local image build
Consumers do not need Upscayl or Flatpak installed on the Docker host. Thesesteps are only for maintainers who want to build the image locally from theircurrently installed Flatpak:
scripts/stage-from-flatpak.sh
scripts/build-local.sh
UPSCAYL_IMAGE=local/upscayl-api:latest \
UPSCAYL_PULL_POLICY=never \
scripts/deploy-api.sh
stage-from-flatpak.sh copies the backend, four reviewed model pairs, andFlatpak provenance from the reviewed org.upscayl.Upscayl Flatpakinto ignored vendor/ paths. It rejects unexpected Flatpak versions, OSTreecommits, manifests, binaries, or model hashes. scripts/build-local.sh refusesto build when any required asset or provenance file is missing. Matching thebuild process does not prove byte-for-byte reproduction of a published artifact;use the recorded Flatpak commit, image digest, and asset hashes for exact provenance.UltraSharp and UltraMix Balanced retain CC BY-NC-SA 4.0/non-commercial terms;see THIRD_PARTY_NOTICES.md before redistributing or using the complete imagecommercially.
NVIDIA/Vulkan compatibility
NVIDIA Container Toolkit 1.19.x on Debian 13 can expose CUDA while omittinglibraries needed by Vulkan. The deployment mounts the exact host-driver versionsof these libraries read-only:
libnvidia-eglcorelibnvidia-glsilibnvidia-tlslibnvidia-glcorelibnvidia-gpucomplibnvidia-glvkspirv
The image also installs libegl1. Re-run both GPU and API verification after anNVIDIA driver or container-toolkit update.
Worker API
GET /healthz— unauthenticated process livenessGET /readyz— authenticated model/GPU readiness and queue depthGET /v1/models— authenticated model capabilitiesPOST /v1/jobs— authenticated multipart submissionGET /v1/jobs/{id}— authenticated statusGET /v1/jobs/{id}/result— authenticated PNG resultDELETE /v1/jobs/{id}— authenticated terminal-job cleanup
POST /v1/jobs accepts scales from 1 through 16, image_type as auto,photo, or digital, and model as auto or an allow-listed ID.
Development
uv sync --frozen --extra test
uv run pytest -q
uv run ruff check upscayl_worker upscayl_bridge tests scripts
uv run ruff format --check upscayl_worker upscayl_bridge tests scripts
See docs/mcp-architecture.md for the implementedintegration boundary and docs/operations.md fordeployment limits, token rotation, cleanup, and release verification.
Licensing
Original repository code and deployment glue are MIT licensed. The publishedcontainer also contains unmodified third-party Upscayl runtime and model assetswith separate licenses and attribution requirements. Models with unresolved ornon-commercial redistribution terms are deliberately excluded. See NOTICE andTHIRD_PARTY_NOTICES.md before redistribution orcommercial use.