100K+
A minimal Docker setup that automatically fetches, updates, and runs the latest urNetwork Provider. The container is built on Alpine Linux, ensuring a minimal footprint. Includes built-in authentication handling, resilient restarts, scheduled in‑container updates (nightly build), and a built-in provider status endpoint.
Payout wallet — highly advised: set your earning wallet in the UrNetwork mobile app or website before expecting rewards. This container does not configure wallet consent; a registered provider without a wallet is not paid.
UPDATE_TIME)userpass (email/password) and authcode8080 (no separate vnStat/httpd)urnetwork user (UID/GID 1000) via gosu| Variable | Requirement | Description |
|---|---|---|
-e AUTH='userpass'-e AUTH='authcode' | Optional | userpass Log in with USER_AUTH and PASSWORD and keep the provider running.authcode Log in with an auth code from https://ur.io/ ( AUTHCODE or the container argument).Default is userpass |
-e USER_AUTH <EMAIL>and -e PASSWORD <PASSWORD> | Required for userpass | Your Email and Password. The container will exit if not provided. |
-e AUTHCODE <AUTH-CODE> | Required for authcode | Your authentication code. The container will exit if not provided. Generate one here: https://ur.io/ |
-e API_URLand -e CONNECT_URL | Optional | Custom operator endpoints, for example API_URL='https://api.example.net' and CONNECT_URL='wss://connect.example.net'.Passed as --api_url and --connect_url to both auth and provide.Set both or neither — if only one is set the container exits instead of starting. Ignored with a warning when ALL_OPERATORS=Y, because each operator uses its own endpoints.Default is empty (the binary defaults to the public operator). |
-e MAX_MEMORY | Optional | Soft memory target passed as --max-memory, for example 1gib.Default is empty (the binary picks a target from host memory). |
-e VERBOSE | Optional | Provider log verbosity: 0 off, 1 for -v, 2 for -vv.Default is 0. |
-e PROVIDER_PORT | Optional | Provider status JSON port, passed as --port. http://localhost:8080/ returns version/status/diagnostics.Set to 0 to disable the status server.Default is 8080. |
-e ALL_OPERATORS | Optional | Y/N. Y passes --all-operators and mines every operator in https://ur.xyz/operators.yml, with a separate state directory per operator.N mines the default operator only.Default is N. |
-e AUTO_REGISTER | Optional | Y/N. Only used with ALL_OPERATORS=Y. Passes --auto-register, which implies --allow-client-registration, and requires HOTKEY_SEED_FILE.Default is N. |
-e HOTKEY_SEED_FILE | Required forAUTO_REGISTER=Y | Absolute path inside the container to an sr25519 hotkey seed file (32 raw bytes or 64 hex characters). The provider never creates this file. |
-e ALLOW_CLIENT_REGISTRATION | Optional | auto/Y/N. Controls --allow-client-registration.auto adds the flag only when /home/urnetwork/.urnetwork has no provider identity yet (a genuinely new install).Y always adds it. N never adds it. Default is auto. |
-e PROVIDER_ARGS | Optional | Extra flags passed to provide, for example --adopt-legacy-provider-key or --max-memory=1gib.Default is empty. |
-e UPDATE_TIME | Optional | Time of the daily in‑container update, HH:MM in UTC.Default is 00:00. |
-v urnetwork_data:/home/urnetwork/.urnetworkor --mount type=volume,src=urnetwork_data,dst=/home/urnetwork/.urnetwork | Recommended | Persists the provider identity, JWT and registration state. Without it, every new container becomes a new provider identity and loses its registration. Both -v and --mount work, with a named volume or a host directory. See Persistent state and mount permissions. |
-v /path/to/your/proxy.txt:/app/proxy.txt | Optional | Mount a proxy configuration file from host to container. Omit this line entirely if you don't want to use a proxy. The proxy inside the proxy.txt should have this format ip:port:username:password. See the sample below. one proxy per line. |
| Optional | 123.456.789.012:55555:myproxyusername:myproxypassword234.567.890.123:44444:myproxyusername:myproxypassword345.678.901.234:33333:myproxyusername:myproxypassword456.789.012.345:22222:myproxyusername:myproxypassword567.890.123.456:11111:myproxyusername:myproxypassword | |
-p 8080:8080 | Optional | Provider status JSON = http://localhost:8080/.Exposing it publicly reveals provider diagnostics; restrict it to your monitoring network. Set PROVIDER_PORT=0 to disable it. |
-v and --mount are equivalent here. All of these work:
# Named volume (Docker manages it; inherits urnetwork:urnetwork, mode 700)
-v urnetwork_data:/home/urnetwork/.urnetwork
--mount type=volume,src=urnetwork_data,dst=/home/urnetwork/.urnetwork
# Host directory (must be owned by UID/GID 1000)
-v /path/to/urnetwork_data:/home/urnetwork/.urnetwork
--mount type=bind,src=/path/to/urnetwork_data,dst=/home/urnetwork/.urnetwork
Prepare a host directory once before using it:
mkdir -p /path/to/urnetwork_data
sudo chown 1000:1000 /path/to/urnetwork_data
sudo chmod 700 /path/to/urnetwork_data
--mount type=bind fails when the host directory does not exist. -v creates a
missing host path as root:root, which the urnetwork user cannot use, so create
it first.
Do not
chmod 777the state directory. The provider requires/home/urnetwork/.urnetworkto be owned by the user it runs as and not writable by group/other. If it is world-writable, authentication fails withpanic: registration parent must be owned and not writable by other users. Usechmod 700instead.
The container starts as urnetwork (UID/GID 1000); no root process is used. Because
a bind mount keeps the host's ownership, its source must be owned by UID/GID 1000
and not writable by group/other. If the source is owned by 1000 but world-writable
(777), startup removes the group/other write bits. If it is owned by another UID,
startup stops with a clear error: chown 1000:1000 <source>.
A named volume inherits ownership and mode from the image
(/home/urnetwork/.urnetwork, urnetwork:urnetwork, mode 700), so it needs no manual preparation.
If you start the container as root (--user 0), the entrypoint drops to urnetwork
with gosu. Other non-root UIDs are rejected, because provider custody is tied to the
runtime UID; chown the bind source to 1000:1000 instead.
Any other files mounted into the container, such as proxy.txt or a hotkey seed, must
be readable by the runtime UID (1000 by default).
Upgrading: the state path moved from
/root/.urnetworkto/home/urnetwork/.urnetwork, and the old files areroot:root. Before starting the new image, give the existing volume or bind source to UID/GID 1000:# Named volume (one-time; --user 0 is used only for this migration) docker run --rm --user 0 -v urnetwork_data:/data \ --entrypoint sh techroy23/docker-urnetwork:latest \ -c 'chown -R 1000:1000 /data && chmod 700 /data' # Host directory sudo chown -R 1000:1000 /path/to/urnetwork_dataOtherwise the container cannot read the old state and registers a new identity.
| Flag | Default | Turn it ON when | Turn it OFF when |
|---|---|---|---|
ALLOW_CLIENT_REGISTRATION | auto | A brand new install with an empty /home/urnetwork/.urnetwork (auto already does this), or you intentionally want a new provider identity/slot. | Restoring an existing installation or any normal restart. Leaving it auto is correct for both cases. |
ALL_OPERATORS | N | You want one container to serve every operator in the published list. Each operator gets its own identity under ~/.urnetwork/operators/<domain>/. | You want a single provider identity on the default operator (normal setup). |
AUTO_REGISTER | N | You use ALL_OPERATORS=Y and want each newly listed operator registered automatically from a Bittensor hotkey (HOTKEY_SEED_FILE required). | You do not use ALL_OPERATORS, or you authenticate operators with your account instead. |
PROVIDER_PORT | 8080 | You want the built-in status/diagnostics JSON. | The port would be publicly reachable, or you do not need monitoring. Use 0. |
API_URL + CONNECT_URL | unset | You serve a non-default operator. Set both together. | You use the public default operator (normal setup). |
MAX_MEMORY | unset | The host has a memory limit and you want the provider to stay under it, e.g. 1gib. | You want the binary to size itself from host memory. |
VERBOSE | 0 | You are debugging startup/connection problems. | Normal operation. |
# Option 1 : amd64 build
docker run -d --platform linux/amd64 \
--name=urnetwork \
--pull=always --restart=always \
--log-driver=json-file --log-opt max-size=1m --log-opt max-file=1 \
--cap-add=NET_ADMIN --cap-add=NET_RAW --sysctl net.ipv4.ip_forward=1 --sysctl net.ipv4.ip_unprivileged_port_start=0 \
-e AUTH='userpass' \
-e USER_AUTH='[email protected]' \
-e PASSWORD='mYv3rYsEcUr3dP@sSw0rD' \
-v /path/to/your/proxy.txt:/app/proxy.txt \
-v urnetwork_data:/home/urnetwork/.urnetwork \
-p 8080:8080 \
techroy23/docker-urnetwork:latest
# Option 2 : arm64 build
docker run -d --platform linux/arm64 \
--name=urnetwork \
--pull=always --restart=always \
--log-driver=json-file --log-opt max-size=1m --log-opt max-file=1 \
--cap-add=NET_ADMIN --cap-add=NET_RAW --sysctl net.ipv4.ip_forward=1 --sysctl net.ipv4.ip_unprivileged_port_start=0 \
-e AUTH='userpass' \
-e USER_AUTH='[email protected]' \
-e PASSWORD='mYv3rYsEcUr3dP@sSw0rD' \
-v /path/to/your/proxy.txt:/app/proxy.txt \
-v urnetwork_data:/home/urnetwork/.urnetwork \
-p 8080:8080 \
techroy23/docker-urnetwork:latest
<AUTH-CODE> with the code you generated from https://ur.io/# Option 1 : amd64 build
docker run -d --platform linux/amd64 \
--name=urnetwork \
--pull=always --restart=always \
--log-driver=json-file --log-opt max-size=1m --log-opt max-file=1 \
--cap-add=NET_ADMIN --cap-add=NET_RAW --sysctl net.ipv4.ip_forward=1 --sysctl net.ipv4.ip_unprivileged_port_start=0 \
-e AUTH='authcode' \
-e AUTHCODE='<AUTH-CODE>' \
-v /path/to/your/proxy.txt:/app/proxy.txt \
-v urnetwork_data:/home/urnetwork/.urnetwork \
-p 8080:8080 \
techroy23/docker-urnetwork:latest
# Option 2 : arm64 build
docker run -d --platform linux/arm64 \
--name=urnetwork \
--pull=always --restart=always \
--log-driver=json-file --log-opt max-size=1m --log-opt max-file=1 \
--cap-add=NET_ADMIN --cap-add=NET_RAW --sysctl net.ipv4.ip_forward=1 --sysctl net.ipv4.ip_unprivileged_port_start=0 \
-e AUTH='authcode' \
-e AUTHCODE='<AUTH-CODE>' \
-v /path/to/your/proxy.txt:/app/proxy.txt \
-v urnetwork_data:/home/urnetwork/.urnetwork \
-p 8080:8080 \
techroy23/docker-urnetwork:latest
docker run -d --platform linux/amd64 \
--name=urnetwork \
--pull=always --restart=always \
--cap-add=NET_ADMIN --cap-add=NET_RAW --sysctl net.ipv4.ip_forward=1 --sysctl net.ipv4.ip_unprivileged_port_start=0 \
-e AUTH='userpass' \
-e USER_AUTH='[email protected]' \
-e PASSWORD='mYv3rYsEcUr3dP@sSw0rD' \
-e ALL_OPERATORS='Y' \
-v urnetwork_data:/home/urnetwork/.urnetwork \
-p 8080:8080 \
techroy23/docker-urnetwork:latest
Each operator is authenticated with your account when its token is missing. To register every operator from a Bittensor hotkey instead, add:
-e ALL_OPERATORS='Y' \
-e AUTO_REGISTER='Y' \
-e HOTKEY_SEED_FILE='/home/urnetwork/.urnetwork/hotkey.seed' \
-v /path/to/hotkey.seed:/home/urnetwork/.urnetwork/hotkey.seed:ro \
When authentication fails, the container now prints the provider's own output, even with
DEBUG unset. Look for the last [ERROR] lines before "Will retry authentication", for example:
[ERROR] panic: registration parent must be owned and not writable by other users
[ERROR] /home/urnetwork/.urnetwork is owned by another user or writable by group/other
In that case fix the mount permissions (chmod 700), not AUTHCODE or the password.
Content type
Image
Digest
sha256:59d46d2bc…
Size
91.5 MB
Last updated
about 16 hours ago
docker pull techroy23/docker-urnetwork