How to connect to a remote host?
Connect to a shard server with its URL and an API token, then use it as if it ran on this machine:
export SHARD_API_KEY=<the token field that shard tokens mint printed>shard setup --remote https://shard.example.com --save -yshard create --name demo --memory 512MiB alpine:3.20shard exec demo echo helloshard remove --force demoimport { Shard } from "useshards";
// Reads SHARD_API_KEY.const shard = new Shard({ remote: "https://shard.example.com" });try { const sandbox = await shard.create({ name: "demo", image: "alpine:3.20", memoryMiB: 512 }); const result = await sandbox.exec(["echo", "hello"]); console.log(result.stdout); await sandbox.remove({ force: true });} finally { shard.close();}from useshards import Shard
# Reads SHARD_API_KEY.with Shard(remote="https://shard.example.com") as shard: sandbox = shard.create("alpine:3.20", name="demo", memory_mib=512) result = sandbox.exec(["echo", "hello"]) print(result.stdout) sandbox.remove(force=True)shard setup --remote checks that it can reach the server, that the server accepts the key, and which verbs the server supports. With --save, it saves the connection, so later commands need no variables.
The server needs three things before a client can connect: a daemon, shard serve, and an HTTPS proxy or a tunnel in front of it. The steps below set them up on a Linux host with systemd.
1. Set up the daemon
Section titled “1. Set up the daemon”Set up the host to run sandboxes, as in Quickstart: server:
shard setup --local --provider gvisor --start-at-boot=true -yThe daemon serves its API on a unix socket on the host, never on TCP. This step does not expose it to the network.
2. Set up the HTTP API
Section titled “2. Set up the HTTP API”shard serve is the HTTP API, and the only way in from the network. It is a separate process that does not run as root. It checks the key of each request, then passes the request to the daemon’s socket. Setup runs it as a background service:
shard setup --local --provider gvisor --http-api=true -yOn a host that setup already set up, this changes only the HTTP API, the Set up the HTTP API row of the existing installation. On a new host, it sets up the daemon too, so step 1 is optional. Without -y, setup asks Set up the HTTP API? after the daemon startup question.
Setup checks the address, the port, the permissions and the service manager before it changes anything, and shows the configuration first. Then it creates the shard account, the token signing key and an API key, and starts the service. The service starts at boot after the daemon, stops with it, and starts again with it. API requests fail while the daemon is stopped. Last, setup sends an authenticated request to prove the API works.
The HTTP API listens on 127.0.0.1:7850 by default, which accepts connections only from the host. Pass --listen with a VPN address, such as --listen 100.64.0.5:7850, to permit direct access through that private network.
The API key lives in /etc/shard/api-key, which only root can read. Setup shows the file, never the key:
sudo cat /etc/shard/api-keyA repeated setup keeps the key. Pass --replace-api-key to replace it, which cuts off every client that uses the old key. On a Mac, the key is /var/lib/shard/auth/api-key and the service is the LaunchDaemon /Library/LaunchDaemons/shard.serve.plist.
By hand
Section titled “By hand”The same service, without setup. Each release ships a systemd unit for it:
sudo groupadd --system shardsudo useradd --system --no-create-home --shell /usr/sbin/nologin --gid shard shardsudo systemctl restart shardsudo install -d -m2750 -o root -g shard /etc/shardsudo sh -c 'umask 077; openssl rand -hex 32 > /etc/shard/serve.secret'sudo chown root:shard /etc/shard/serve.secretsudo chmod 0640 /etc/shard/serve.secretcurl -fsSLO https://github.com/presmihaylov/shard/releases/latest/download/shard-serve.servicesudo install -m0644 shard-serve.service /etc/systemd/system/shard-serve.servicesudo systemctl daemon-reloadsudo systemctl enable --now shard-serveThe daemon gives its socket to the shard group only when it starts, so the commands restart it once after they create the group. The unit runs as the shard user and group, and reads the token signing key from /etc/shard/serve.secret. It starts after the daemon, stops with it, and starts again with it. Uninstall keeps this unit, because setup did not install it.
3. Put HTTPS in front
Section titled “3. Put HTTPS in front”shard serve has no TLS, and setup does not configure HTTPS. For one client, an SSH tunnel is enough. Run this on the client, then use http://127.0.0.1:7850 as the server URL:
ssh -N -L 7850:127.0.0.1:7850 <user>@<server>For more clients, put a proxy or a tunnel in front of it that gives clients an https URL on port 443. The proxy must pass WebSocket upgrades through and must not buffer responses. Otherwise shard exec, shard shell and shard logs -f stall. It must not log the Authorization header, because that header carries the token.
shard.example.com { reverse_proxy 127.0.0.1:7850 { flush_interval -1 }}Caddy gets a certificate from a public CA for a public name. On a host with no public name, add tls internal inside the site block, and give clients the root certificate of Caddy’s own CA in SHARD_CA_FILE.
tunnel: <tunnel id>credentials-file: /etc/cloudflared/<tunnel id>.jsoningress: - hostname: shard.example.com service: http://127.0.0.1:7850 - service: http_status:404A tunnel needs no inbound port open on the host.
tailscale serve --bg --https=443 http://127.0.0.1:7850Clients on the same tailnet use https://<host>.<tailnet>.ts.net.
shard serve sees every client as one source: the proxy. It holds at most 32 connections from one source that have not yet shown a valid token, so this bound applies to all clients together. Rate-limit floods at the proxy.
4. Mint a token
Section titled “4. Mint a token”Mint one token for each client, on the server:
sudo shard tokens mint --name laptop --duration 720h \ --scopes sandbox:read,sandbox:write,sandbox:delete,exec \ --signing-key-file /etc/shard/serve.secret{"token":"<jwt>","expires_at":"2026-11-04T18:00:00Z","scopes":["sandbox:read","sandbox:write","sandbox:delete","exec"]}Setup’s own key, shard-setup, can do everything and never expires. Mint a narrower token for each client instead.
The token field is the client’s API key. Give shard tokens mint, list and revoke the same --signing-key-file, so the token lands in the ledger that shard serve reads.
Without --scopes, a token can do everything, secrets and policies included. Without --duration, it never expires. Authentication lists the scopes and what each one allows.
5. Connect a client
Section titled “5. Connect a client”Set the key in SHARD_API_KEY, never as a command argument. Then save the connection:
shard setup --remote https://shard.example.com --save -ySetup writes the server URL and the key to ~/.config/shard/config.json by default, and only your user can read the file. To connect without saving anything, set SHARD_REMOTE and SHARD_API_KEY instead. The SDKs read the same two variables, or take remote and apiKey (remote= and api_key= in Python). They never read the saved connection.
If a private CA signed the server’s certificate, set SHARD_CA_FILE to that CA’s certificate. shard always verifies the certificate, and has no option to skip it. Environment and config describes SHARD_REMOTE, SHARD_API_KEY and SHARD_CA_FILE.
An http URL works too, but nothing encrypts the key or the requests. Use it only on localhost or through an encrypted VPN. Each command then prints a warning to stderr.
Revoke a token
Section titled “Revoke a token”List the tokens, then revoke one by its ID, or every token of a name:
sudo shard tokens list --signing-key-file /etc/shard/serve.secretsudo shard tokens revoke --name laptop --signing-key-file /etc/shard/serve.secretshard serve refuses a revoked token on the next request, with no restart. It also closes any open stream that uses that token, such as shard exec, shard shell or shard logs -f, within a second. The command and its sandbox keep running.
Commands that run on the server only
Section titled “Commands that run on the server only”These commands refuse a remote server: shard pull, shard image list, shard image remove, shard image prune, shard daemon, shard daemon status, shard info, shard serve, and shard tokens mint, list and revoke. Run them on the server.
On a machine with a saved connection, add --remote "" to send one command to this machine’s daemon:
shard --remote "" list