This page is in English — onze technische documentatie voor ontwikkelaars en beheerders is in het Engels.
Run Relay with Docker
Deploy Viaalink Relay and Caddy reverse proxy using Docker Compose with automatic HTTPS.
Container deployment overview
The Viaalink Relay container image (viaalink/relay, for amd64 and arm64) is published with the first release — ask contact@viaalink.com for early access. The Docker Compose setup below is what you will run once the image is published.
In production, running Viaalink Relay behind Caddy provides automatic Let’s Encrypt certificate issuance, wildcard subdomain management, and HTTP/3 support while Relay manages SSH reverse tunnels and routing.
Prerequisites
- Docker Engine 24.0+ and Docker Compose v2.0+.
- Domain with DNS pointing to your host server (see DNS Configuration):
relay.example.com(A / AAAA)*.relay.example.com(A / AAAA or CNAME)
- Open ports on your firewall or router:
80/tcp,443/tcp,2222/tcp, and optionally3478/udp.
Deployment files
Create a dedicated directory on your server (for example /opt/viaalink-relay) containing the following configuration files:
mkdir -p /opt/viaalink-relay/data/{keys,ssl,pubkeys,license}
cd /opt/viaalink-relay1. Caddy build with DNS-01 provider plugins (Dockerfile.caddy)
Issuing Let’s Encrypt wildcard certificates (*.relay.example.com) requires ACME DNS-01 validation. Because the stock caddy:2-alpine image does not bundle DNS provider plugins, build a small Caddy image with xcaddy including the five supported DNS providers:
Create Dockerfile.caddy:
FROM caddy:2-builder-alpine AS builder
RUN xcaddy build \
--with github.com/caddy-dns/porkbun \
--with github.com/caddy-dns/cloudflare \
--with github.com/caddy-dns/route53 \
--with github.com/caddy-dns/digitalocean \
--with github.com/caddy-dns/godaddy
FROM caddy:2-alpine
COPY --from=builder /usr/bin/caddy /usr/bin/caddy2. Docker Compose file (docker-compose.yml)
Create docker-compose.yml (note that ./data/config.yml is mounted writable so changes saved in /_relay/dashboard persist to disk, and VIAALINK_RELAY_LICENSE_KEY_FILE points to /app/deploy/license/license.jwt):
services:
relay:
image: viaalink/relay:latest
container_name: viaalink-relay
restart: unless-stopped
ports:
- "2222:2222"
- "3478:3478/udp"
environment:
VIAALINK_RELAY_DOMAIN: relay.example.com
VIAALINK_RELAY_SSH_ADDRESS: ":2222"
VIAALINK_RELAY_HTTP_ADDRESS: ":8080"
VIAALINK_RELAY_HTTPS_ADDRESS: ":8443"
VIAALINK_RELAY_AUTHENTICATION: "true"
VIAALINK_RELAY_BIND_RANDOM_SUBDOMAINS: "true"
VIAALINK_RELAY_LICENSE_KEY_FILE: "/app/deploy/license/license.jwt"
VIAALINK_RELAY_LOG_JSON: "true"
VIAALINK_RELAY_LOG_TO_STDOUT: "true"
volumes:
- ./data/keys:/app/deploy/keys
- ./data/pubkeys:/app/deploy/pubkeys
- ./data/ssl:/app/deploy/ssl
- ./data/license:/app/deploy/license
- ./data/config.yml:/app/config.yml
networks:
- relay-net
caddy:
build:
context: .
dockerfile: Dockerfile.caddy
container_name: viaalink-caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
environment:
CLOUDFLARE_API_TOKEN: your-cloudflare-api-token
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
networks:
- relay-net
depends_on:
- relay
networks:
relay-net:
driver: bridge
volumes:
caddy-data:
caddy-config:3. Caddy reverse proxy configuration (Caddyfile)
Create Caddyfile in the same directory with the tls DNS-01 challenge block for your DNS provider (Cloudflare shown below):
{
email admin@example.com
servers {
protocols h1 h2 h3
}
}
# Primary host and wildcard subdomains (DNS-01 required for *.relay.example.com)
relay.example.com, *.relay.example.com {
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
reverse_proxy relay:8080 {
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-Host {host}
header_up X-Forwarded-Port {port}
}
encode gzip
}4. Initial Relay configuration (data/config.yml)
Create data/config.yml:
domain: relay.example.com
ssh-address: :2222
http-address: :8080
https-address: :8443
authentication: true
bind-random-subdomains: true
log-json: true
log-to-stdout: true
private-keys-directory: /app/deploy/keys
authentication-keys-directory: /app/deploy/pubkeys
https-certificate-directory: /app/deploy/ssl
license-key-file: /app/deploy/license/license.jwtStarting the stack
Build and start both containers in detached mode:
docker compose up -d --buildCheck the status of running containers:
docker compose psView the Relay server logs to verify startup and retrieve the initial setup code:
docker compose logs -f relayOpen the setup dashboard at https://relay.example.com/_relay/dashboard?x-authorization=<setup-code> (or http://<server-ip>:8080/_relay/dashboard?x-authorization=<setup-code> if port 8080 is exposed on your LAN).
Persistent volumes and storage
All critical persistent state is preserved in the ./data directory on the host:
| Host Directory | Container Path | Purpose |
|---|---|---|
./data/keys/ | /app/deploy/keys | Server SSH private host keys (generated on first boot) |
./data/pubkeys/ | /app/deploy/pubkeys | Authorized client SSH public keys |
./data/ssl/ | /app/deploy/ssl | Custom SSL certificates and keys |
./data/license/ | /app/deploy/license | License file (license.jwt) and setup-token.txt |
./data/config.yml | /app/config.yml | Writable Relay configuration file (updated by /_relay/dashboard) |
caddy-data volume | /data | Caddy Let’s Encrypt certificates and account keys |
DNS-01 provider credentials
When configuring the tls { dns ... } directive in Caddyfile (or when Relay drives a bundled Caddy binary with --caddy-https-enabled=true), use the environment variables for your provider:
| Provider | Caddyfile dns directive | Environment Variables |
|---|---|---|
| Porkbun | dns porkbun { api_key {env.PORKBUN_API_KEY} api_secret_key {env.PORKBUN_API_SECRET_KEY} } | PORKBUN_API_KEY, PORKBUN_API_SECRET_KEY |
| Cloudflare | dns cloudflare {env.CLOUDFLARE_API_TOKEN} | CLOUDFLARE_API_TOKEN |
| AWS Route53 | dns route53 { access_key_id {env.AWS_ACCESS_KEY_ID} secret_access_key {env.AWS_SECRET_ACCESS_KEY} region {env.AWS_REGION} } | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION |
| DigitalOcean | dns digitalocean {env.DIGITALOCEAN_API_TOKEN} | DIGITALOCEAN_API_TOKEN |
| GoDaddy | dns godaddy {env.GODADDY_API_TOKEN} | GODADDY_API_TOKEN |
Upgrading
To update Viaalink Relay and Caddy to the latest releases:
# Pull new images and rebuild Caddy if needed
docker compose pull
docker compose up -d --buildExisting client connections will automatically reconnect when the new container starts.
Verifying health
Operational health is verified when:
- Port
2222responds to SSH connections:bashssh -p 2222 relay.example.com - Your browser reaches
https://relay.example.com/_relay/dashboardor a published test application over HTTPS.
Next steps
- Manage connecting devices and public keys in Access Control and Security.
- Learn about offline plan activation in Relay Licensing.
- Connect developer devices with Install the Viaalink CLI.