Installation
Install the edge first, then the client. The edge is the security boundary, so avoid per-client overrides for daemon config in production.
Build binaries
Section titled “Build binaries”From the repo root:
make buildThis creates:
bin/mytunnelbin/mytunneld
Without local Go, build in Docker:
mkdir -p bindocker run --rm -v "$PWD":/workspace -w /workspace golang:1.25 \ sh -lc 'export PATH=/usr/local/go/bin:$PATH; go build -o bin/mytunnel ./cmd/mytunnel && go build -o bin/mytunneld ./cmd/mytunneld'Configure wildcard DNS
Section titled “Configure wildcard DNS”Create a wildcard record for the base domain:
*.tunnel.example.com -> <EDGE_IP>Also keep the apex or a health hostname available for checking the edge:
edge-health.tunnel.example.com -> <EDGE_IP>Choose a Caddy ACME provider
Section titled “Choose a Caddy ACME provider”If you want Caddy to issue wildcard certificates, build or install Caddy with the DNS module for the provider that hosts your DNS zone.
Common module names:
| Provider | Caddy DNS module |
|---|---|
| Cloudflare | dns.providers.cloudflare |
| Route53 | dns.providers.route53 |
| Google Cloud DNS | dns.providers.googleclouddns |
| DigitalOcean | dns.providers.digitalocean |
| OVH | dns.providers.ovh |
| Infomaniak | dns.providers.infomaniak |
Build Caddy with a provider module:
xcaddy build --with MODULE_PATH@latestsudo install -m 0755 ./caddy /usr/bin/caddyMinimal Caddyfile shape:
{ admin 127.0.0.1:2019 acme_dns <provider> {env.<PROVIDER_TOKEN_ENV>}}
edge-health.tunnel.example.com { respond "mytunnel edge is online" 200}Validate and restart Caddy:
sudo caddy fmt --overwrite /etc/caddy/Caddyfilesudo caddy validate --config /etc/caddy/Caddyfilesudo systemctl restart caddyInstall the edge with Infomaniak Caddy bootstrap
Section titled “Install the edge with Infomaniak Caddy bootstrap”On an apt-based edge server:
cd ~/tunneledsudo ./scripts/install-edge.sh \ --base-domain tunnel.example.com \ --binary ./bin/mytunneld \ --infomaniak-token '<INFOMANIAK_API_TOKEN>'This script:
- Installs base packages with
apt-get. - Builds Caddy with the Infomaniak DNS module if needed.
- Writes
/etc/caddy/Caddyfile. - Writes a restricted systemd drop-in for
INFOMANIAK_API_TOKENwhen provided. - Calls
scripts/bootstrap-edge.sh. - Starts Caddy and
mytunneld.
If you need a different Caddy provider, skip Caddy and bootstrap only the daemon:
sudo ./scripts/install-edge.sh \ --base-domain tunnel.example.com \ --binary ./bin/mytunneld \ --skip-caddyBootstrap only mytunneld
Section titled “Bootstrap only mytunneld”Use this when Caddy is already installed and configured:
sudo ./scripts/bootstrap-edge.sh tunnel.example.com ./bin/mytunneldUseful flags:
| Flag | Meaning |
|---|---|
--run-user <user> | systemd runtime user, default mytunnel |
--run-group <group> | runtime group, default primary group of the run user |
--no-create-user | fail instead of creating the runtime user |
Bootstrap writes /etc/mytunneld/mytunneld.env with restricted permissions and installs /usr/local/bin/mytunneld.
Install the client
Section titled “Install the client”On the source machine:
./scripts/install-local.sh --binary ./bin/mytunnelCompatibility wrapper:
./scripts/install-client.sh ./bin/mytunnelBoth paths install /usr/local/bin/mytunnel.
Add a forced-command SSH key
Section titled “Add a forced-command SSH key”For a dedicated tunnel user:
sudo ./scripts/install-edge-ssh-key.sh mytunnel-client ./id_ed25519.pubThe installer writes an authorized_keys entry with:
command="/usr/local/libexec/mytunnel-ssh-gate.sh"no-agent-forwardingno-X11-forwardingno-user-rcno-ptypermitlisten="127.0.0.1:*"
It also adds the SSH user to the daemon run group so control commands can read config and update the SQLite DB.
After SSH config changes, confirm AllowTcpForwarding yes and reload sshd.
Verify installation
Section titled “Verify installation”Edge:
sudo ./scripts/check-edge.shClient:
./scripts/check-client.sh --edge mytunnel-client@edge.example.comDaemon health:
mytunneld ctl health --json