manual setup · copy-paste · ~10 minutes

Set up your Relayer,
command by command

Every step to go from zero to a running, claimed Relayer with S3 storage you can use. Copy each command, run it, check the green box, move on. You’ll need Docker on a machine you control — that’s it.

~10 minutes start to finish 6 copy-paste steps Docker is all you install ends with working S3 storage

Docker
The only prerequisite

8888 + 9000
Web UI & S3 ports

10 GB
Free disk (app + database)

No login
Public images, anonymous pull

System requirements

Modest — it runs on a laptop or a small VM.

Operating System

Win / Mac / Linux

Windows 10/11, macOS 12+, Ubuntu 22.04+

Memory

8 GB recommended

4 GB minimum

Free disk

10 GB

app + local database

Ports

8888 / 9000 / 9443

dashboard, S3 HTTP, S3 HTTPS

Step 1 of 6

Install Docker

The Relayer runs inside Docker. Install it once and never think about it again.

Windows or macOS: download Docker Desktop, install, launch it, and wait until it says “running.”
Linux: follow the official Docker Engine install guide. Make sure the docker compose plugin is included (it is in current installs).

verify Docker is running
docker --version

✓ Prints a version number? Docker is ready. “Command not found” means Docker Desktop isn’t running yet.

Step 2 of 6

Create your XNS account

Your Relayer needs an owner. Create a free XNS account so your storage is bound to you.

Create your XNS account →
You’ll see a secure sign-up form. Fill it in, then check your email and click the verification link. That’s it — no credit card, no trial timer.

Prefer the terminal? If you installed the xns CLI:

register from the command line (optional)
xns register \
  --endpoint https://console.xns.tech \
  --email you@example.com \
  --password yourpassword \
  --first-name Alice \
  --last-name Example

✓ Email verified? Your account is live. Next: bring up your Relayer.

Step 3 of 6

Download and start the Relayer

One compose file, one command. The images are public — no registry login needed.

Create a folder and save the compose file:

create the folder
mkdir -p ~/xns-relayer && cd ~/xns-relayer

Save this as docker-compose.yml inside that folder:

docker-compose.yml — copy the whole block
services:
  relayer:
    container_name: xns-relayer
    image: releases.scpri.me/xns-relayer:release-latest
    pull_policy: always
    restart: unless-stopped
    privileged: true
    command: >
      sh -c '
      if [ "${RELAYER_AUDIT_MODE:-loki}" = "loki" ]; then
        export S3GW_AUDIT_NDJSON_PATH=/var/log/relayer-audit/audit.ndjson;
      fi;
      exec /xns/xns.sh
      '
    ports:
      - "${UI_PORT:-8888}:8888"   # web UI
      - "${S3_PORT:-9000}:9000"   # S3 API (HTTP)
      - "${S3_TLS_PORT:-9443}:9443"   # S3 API (HTTPS, once you install a cert)
    environment:
      - NODE_ENV=production
      - XNS_RELEASE_CHANNEL=release
      - UI_PORT=${UI_PORT:-8888}   # same expression as the ports line above — keeps the logged port honest
      - S3GW_AUDIT_WEBHOOK_URL=       # webhook audit mode only — see .env below
      - S3GW_AUDIT_WEBHOOK_SECRET=
    volumes:
      - relayer_data:/relayer
      - ./data:/relayer-legacy-check:ro   # upgrade safety: old bind-mount data is detected, never overwritten
      - /mnt:/mnt:rslave
      - audit_logs:/var/log/relayer-audit
      - relayer_logs:/relayer/log   # app logs on their own volume, so the log shipper never sees config/secrets
    networks:
      default:
        ipv4_address: 172.28.0.10   # pinned address — Grafana dashboard sign-on trusts exactly this IP
  # Audit-log stack (default on) — ships your S3 audit trail to an on-box Loki.
  # Controlled by RELAYER_AUDIT_MODE in the .env below; set it to off or webhook to skip.
  loki:
    image: releases.scpri.me/relayer-loki:release-latest
    pull_policy: always
    restart: unless-stopped
    profiles: ["loki"]
    command:
      - -config.file=/etc/loki/loki.yml
    volumes:
      - loki_data:/loki
  alloy:
    image: releases.scpri.me/relayer-alloy:release-latest
    pull_policy: always
    restart: unless-stopped
    profiles: ["loki"]
    command:
      - run
      - --server.http.listen-addr=0.0.0.0:12345
      - /etc/alloy/config.alloy
    environment:
      - S3GW_AUDIT_LOKI_PUSH_URL=     # optional: point audit logs at your own Loki/SIEM
    depends_on:
      - loki
    volumes:
      - audit_logs:/var/log/relayer-audit:ro
      - relayer_logs:/relayer/log:ro   # application logs → searchable in Grafana, read-only
  audit-logrotate:
    image: releases.scpri.me/relayer-audit-logrotate:release-latest
    pull_policy: always
    restart: unless-stopped
    profiles: ["loki"]
    volumes:
      - audit_logs:/var/log/relayer-audit
  prometheus:
    container_name: prometheus
    image: releases.scpri.me/relayer-prometheus:release-latest
    pull_policy: always
    restart: unless-stopped
    # Alert thresholds — leave empty for sensible defaults; tune in the .env, then recreate this service
    environment:
      - ALERT_DISK_FREE_BYTES=${ALERT_DISK_FREE_BYTES:-}
      - ALERT_DISK_FREE_FOR=${ALERT_DISK_FREE_FOR:-}
      - ALERT_S3_UNREACHABLE_FOR=${ALERT_S3_UNREACHABLE_FOR:-}
      - ALERT_S3_ERROR_RATE=${ALERT_S3_ERROR_RATE:-}
      - ALERT_S3_ERROR_RATE_FOR=${ALERT_S3_ERROR_RATE_FOR:-}
      - ALERT_S3_P95_LATENCY_SECONDS=${ALERT_S3_P95_LATENCY_SECONDS:-}
      - ALERT_S3_P95_LATENCY_FOR=${ALERT_S3_P95_LATENCY_FOR:-}
      - ALERT_HOSTIO_DOWN_FOR=${ALERT_HOSTIO_DOWN_FOR:-}
      - ALERT_GATEWAY_DOWN_FOR=${ALERT_GATEWAY_DOWN_FOR:-}
      - ALERT_CRASH_LOOP_FOR=${ALERT_CRASH_LOOP_FOR:-}
      - ALERT_CACHE_PRESSURE_RATIO=${ALERT_CACHE_PRESSURE_RATIO:-}
      - ALERT_CACHE_PRESSURE_FOR=${ALERT_CACHE_PRESSURE_FOR:-}
      - ALERT_BRIDGE_STALE_SECONDS=${ALERT_BRIDGE_STALE_SECONDS:-}
      - ALERT_BRIDGE_STALE_FOR=${ALERT_BRIDGE_STALE_FOR:-}
      - ALERT_TARGET_DOWN_FOR=${ALERT_TARGET_DOWN_FOR:-}
      - ALERT_AUDIT_SIEM_DEAD_FOR=${ALERT_AUDIT_SIEM_DEAD_FOR:-}
    volumes:
      - prometheus_data:/prometheus
  alertmanager:
    # Alert delivery — routes firing alerts to your webhook/email; works out of the box, receivers optional
    container_name: alertmanager
    image: releases.scpri.me/relayer-alertmanager:release-latest
    pull_policy: always
    restart: unless-stopped
    environment:
      - ALERTMANAGER_WEBHOOK_URL=${ALERTMANAGER_WEBHOOK_URL:-}
      - ALERTMANAGER_GROUP_WAIT=${ALERTMANAGER_GROUP_WAIT:-}
      - ALERTMANAGER_REPEAT_INTERVAL=${ALERTMANAGER_REPEAT_INTERVAL:-}
      - ALERTMANAGER_SMTP_SMARTHOST=${ALERTMANAGER_SMTP_SMARTHOST:-}
      - ALERTMANAGER_SMTP_FROM=${ALERTMANAGER_SMTP_FROM:-}
      - ALERTMANAGER_SMTP_TO=${ALERTMANAGER_SMTP_TO:-}
      - ALERTMANAGER_SMTP_AUTH_USERNAME=${ALERTMANAGER_SMTP_AUTH_USERNAME:-}
      - ALERTMANAGER_SMTP_AUTH_PASSWORD=${ALERTMANAGER_SMTP_AUTH_PASSWORD:-}
      - WATCHDOG_HEARTBEAT_URL=${WATCHDOG_HEARTBEAT_URL:-}
      - WATCHDOG_REPEAT_INTERVAL=${WATCHDOG_REPEAT_INTERVAL:-}
    volumes:
      - alertmanager_data:/alertmanager   # silences survive restarts
  grafana:
    image: releases.scpri.me/relayer-grafana:release-latest
    pull_policy: always
    restart: unless-stopped
    environment:
      # Dashboards sign you in through the Relayer UI — no separate Grafana login, no anonymous access
      - GF_AUTH_ANONYMOUS_ENABLED=false
      - GF_AUTH_PROXY_ENABLED=true
      - GF_AUTH_PROXY_HEADER_NAME=X-WEBAUTH-USER
      - GF_AUTH_PROXY_HEADER_PROPERTY=username
      - GF_AUTH_PROXY_AUTO_SIGN_UP=true
      - GF_AUTH_PROXY_WHITELIST=172.28.0.10
      - GF_USERS_AUTO_ASSIGN_ORG_ROLE=Admin
      - GF_SERVER_SERVE_FROM_SUB_PATH=true
      - GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s/grafana
    volumes:
      - grafana_data:/var/lib/grafana
  node-exporter:
    image: prom/node-exporter:v1.11.1
    restart: unless-stopped
    command:
      - --path.rootfs=/host
    volumes:
      - /:/host:ro,rslave
volumes:
  relayer_data:
  relayer_logs:
  prometheus_data:
  grafana_data:
  alertmanager_data:
  audit_logs:
  loki_data:
networks:
  # fixed private subnet so the Relayer keeps the pinned address Grafana trusts
  default:
    driver: bridge
    ipam:
      config:
        - subnet: 172.28.0.0/24
          gateway: 172.28.0.1

And save this as .env in the same folder — it switches the audit-log stack (leave it as-is for the default):

.env — audit mode switch (required)
# loki (default) = on-box audit-log stack · webhook = POST to your SIEM · off = no audit trail
#RELAYER_AUDIT_MODE=off
#S3GW_AUDIT_WEBHOOK_URL=
#S3GW_AUDIT_WEBHOOK_SECRET=
# Never edit the line below and never set COMPOSE_PROFILES yourself.
COMPOSE_PROFILES=${RELAYER_AUDIT_MODE:-loki}

Now start it:

start the Relayer
docker compose up -d

First run pulls the images (~a few hundred MB). Give it a minute or two, then open http://localhost:8888 in your browser.

✓ Web UI loads? Your Relayer is running. If ports are taken, see Troubleshooting below.

Step 4 of 6

Claim your Relayer

Claiming binds this running Relayer to your xns account. Sign in, click Connect to xns, and confirm from any device that’s already signed in.

  1. Open the Relayer dashboard at localhost:8888. It sends you to the xns sign-in — sign in with the account from Step 2 (you can create one there too).
  2. Back on the dashboard, a “Welcome to your Relayer” panel appears. Click “Connect to xns.”
  3. A claim link appears under “Complete this on another device.” Open it on any device where you’re signed in to your xns account. The link expires in 15 minutes.
  4. The Relayer detects the claim and the panel closes by itself — the full dashboard is now yours, and storage services start automatically.

Re-deploying a Relayer you already own? Click “I already have an account” on that first panel to reconnect it instead.

✓ The “Welcome” panel closed and the full dashboard loaded? Your Relayer is claimed and connected to the network. On the Dashboard, the Subsystems panel shows the core services turn green as they come up.

Step 5 of 6

Verify S3 storage

Your Relayer is claimed and running. Make sure S3 works by creating a bucket and uploading a test file.

Create S3 credentials in the dashboard: open Users & Groups in the sidebar, go to the Users tab, and click Create User. A username is all you need — the Access Key ID and Secret Access Key are generated for you and shown once, so copy or download them before you close the dialog. Then point any S3 client at your Relayer:

Configure the AWS CLI with those keys (aws configure — region us-east-1), then run the round-trip:

S3 round-trip test — AWS CLI
# create a bucket
aws s3 --endpoint-url http://localhost:9000 mb s3://my-first-bucket
# upload a file
echo "hello XNS" > /tmp/test.txt
aws s3 --endpoint-url http://localhost:9000 cp /tmp/test.txt s3://my-first-bucket/
# list it
aws s3 --endpoint-url http://localhost:9000 ls s3://my-first-bucket/
# download it back
aws s3 --endpoint-url http://localhost:9000 cp s3://my-first-bucket/test.txt /tmp/test-downloaded.txt

✓ File uploaded and downloaded? Your S3 storage is working. rclone, restic, Veeam and any other S3 tool work the same way — same endpoint, same keys.

Prefer a purpose-built CLI? Download the xns client, point it at your Relayer once, and the commands get shorter:

xns CLI — optional
# download (Linux x86-64; macOS Apple Silicon: swap linux-amd64 -> darwin-arm64)
curl -fL https://releases.scpri.me/xns-cli/beta/linux-amd64/xns -o xns && chmod +x xns
# point it at your Relayer with the keys from above
./xns auth login --endpoint http://localhost:9000 --access-key YOUR_ACCESS_KEY --secret-key YOUR_SECRET_KEY
# same round-trip, shorter
./xns mb s3://my-first-bucket
./xns cp /tmp/test.txt s3://my-first-bucket/
./xns ls s3://my-first-bucket/

Step 6 of 6

You’re storing data

That’s it. Your Relayer is running, claimed, and serving S3. Here’s what you have:

WhatWhere
Web UIhttp://localhost:8888
S3 endpoint (HTTP)http://localhost:9000
S3 endpoint (HTTPS)https://localhost:9443 (after installing a cert)
Access keysUsers & Groups → Users tab → Create User
CLI credentials~/.xns/credentials

What’s next:

  • Point your backup tool, S3 SDK, or scripts at http://localhost:9000 and start using it.
  • To update to the latest build: docker compose pull && docker compose up -d (data is untouched).
  • Explore the web UI — monitoring, host preferences (VPD), and certificate management are all there.
  • Turn on encryption: give your Relayer a TLS certificate — automatic Let’s Encrypt, or bring your own.
  • Read the docs for deeper topics.

Troubleshooting

Common issues and quick fixes

Something else is using 8888, 9000, or 9443. Add different port numbers to the .env file you created next to docker-compose.yml, then run docker compose up -d again. (Portainer commonly grabs 9443.)

.env — override ports
UI_PORT=8889
S3_PORT=9001
S3_TLS_PORT=9444

Docker Desktop isn’t running. Open it and wait for the “running” indicator, then retry. On Linux, you may need sudo or to add yourself to the docker group.

Check it’s http:// (not https) at port 8888. Run docker compose ps to confirm the container is running. If it’s restarting, grab the logs: docker compose logs --tail=100.

Storage services start after the Relayer is claimed (Step 4). If you haven’t claimed yet, S3 won’t serve. After claiming, give it 30–60 seconds to connect to the network.

docker compose down -v removes all containers AND data. Use it only as a last resort — it wipes your database, config, claim, and stored objects.

Your private S3 is running

Point any S3 client at your endpoint and start storing. No egress fees, no lock-in, no surprises.

Claims on this page last verified
© Copyright - SCP, Corp | Xa Net Services and Affiliates