docs/user/servers/add-a-server.md
title: Add a server description: What a server needs, the command to paste on it, and the ports to open.
Your apps run on your own servers. To add one, you paste a single command into it, and it joins your other servers on a private network.
What you need
- A Linux server with systemd, amd64 or arm64, from any provider: a Hetzner Cloud server, a DigitalOcean Droplet, a bare-metal box.
- cgroup v2 to run builds. Every current distribution uses it. A server on cgroup v1 joins and runs apps, but its builds fail.
- Ubuntu LTS, Debian 12–13 or Amazon Linux 2023 on a VM or bare metal for managed volumes. Anything else works without them.
- A main disk of 30 GiB or more. Ployz keeps a quarter of the disk, at least 10 GiB, free for the system. On EC2, raise the root volume from its 8 GB default when you launch the instance.
- Root, or a user that can run
sudo. - Outbound internet access, and a public IP address on at least one server so visitors can reach your apps.
You don't need Docker: Ployz installs it if it's missing.
Add one server that joins right away
From your own computer, signed in with ployz login:
| 1 | ployz server add --command |
It prints a line to paste on the server and an enrollment token beside it, then waits until the
server joins. Paste the line in a shell on the server, as root or as a user with sudo, and paste
the token when it asks. The prompt doesn't show what you paste, so the token never enters the
server's shell history or process list. The token works for 24 hours.
A script, such as cloud-init or ssh host '...', has no terminal to answer the prompt. There,
export PLOYZ_ENROLL_TOKEN=pmet_… before the line: it keeps that variable through sudo. You can
also pipe the token to sudo ployz server add --token-stdin after the install part of the line.
The server takes its hostname as its name, lowercased, with anything else a name can't hold, such
as _ or ., turned into -; HP-WIN11 becomes hp-win11. The line prints the name it chose.
To choose another, run
ployz server add --command --name web-1 and the printed line keeps the name. It keeps every other
flag that shapes the server too, such as --storage, --label-add or --accepts-ingress. When it
finishes, the server shows on the Servers page, ready for builds, services and web traffic. If
it doesn't show up, see Troubleshooting servers.
Add more servers the same way, even at different providers. Nothing moves onto a new server until you deploy; see Scaling.
Add servers from the dashboard
Before your first server, the Servers page shows one card instead of a list. Click
Get install command and it makes a token that works for 24 hours and shows the install line
with the token under it, then Shown once. Expires in 24h. Copy the token now: Ployz can't show
it again. Paste the line in a shell on the server, as root or as a user with sudo, and paste the
token when it asks. The card then says Listening for servers… until the first one checks in.
Only an owner or admin, or Ployz support, can make a token. For a member, Get install command and Add servers say so instead.
Once you have a server, or one is waiting, make more tokens with Add servers:
- Go to Servers and click Add servers. While you have no server, the bottom bar of your environment offers it too.
- Name the token, or leave the name blank and Ployz picks one. Choose how long it works: 1 day, 7 days or 30 days.
- Click Create. The Shell tab shows the install line and the token under it, then Shown once. Expires in 24h. Copy the token now: Ployz can't show it again.
- Open a shell on the server, as root or as a user with
sudo, and paste the line. When it asks for the Fleet Token, paste the token. The prompt doesn't show what you paste, so the token stays out of shell history and the process list. To put it in user data instead, take it from the cloud-init tab; it holds the same token. Not on Ubuntu, Debian or Amazon Linux, or can't run ZFS? See Start without ZFS.

Run the same line, and paste the same token, on as many servers as you like while the token works. Each one installs Docker and Ployz and shows up under Waiting to join on the Servers page within seconds, in a tray above your other servers. The tray shows only while a server waits or enrolls. Its header counts the servers that are ready, preparing, blocked and offline, with a bar for the mix. New ones go to the bottom of the list, and the list never reorders while you look at it. When your very first server arrives ready, the tray names it and offers Enroll and Reject right in the header.
Select a waiting server and click Enroll to choose its name, storage and what it runs. You can enroll while it prepares. Only an owner or admin, or Ployz support, can enroll or reject servers. For a member, Enroll and Reject are disabled and say so. Follow Add many servers for the full review and enrollment flow.
Each row shows the server's name and operating system, the IP address Ployz Cloud saw it check in
from and its provider, its CPU, memory and main disk size, and its status. Installing Docker means it is
still getting ready, and Ready means Docker is in place. Building ZFS under Ready means
its ZFS build is still running, and an enrollment with ZFS volumes waits for it. Blocked rows say what
is wrong with the server, in the server's own words, such as Disk is 20 GiB. Ployz needs 30 GiB.
A server that still belongs to another organization is blocked until you run ployz server rm for it there, and
Offline means it stopped checking in. You can't select a blocked server. Once more than ten
servers wait, filter the list by readiness, provider and token, or search by name or IP address.
Servers you enroll move to Enrolling in the same tray, with the step each one is on, until they join.
Above your servers, Fleet counts how many are online, building, offline and unknown, and how many need an upgrade.
Click a waiting row to see the server in full, with its CPU, memory, disks, Docker, the storage it would get, its open ports and anything blocking it. Reject there turns the server away: it stops checking in, and asking again with the same token gets it turned away.
Your tokens
Tokens on the Servers page lists your Fleet Tokens with how many servers wait on each and how many joined, when each expires and when it was last used. Revoke asks you to confirm, then stops a token at once and drops the servers still waiting on it; the message after says how many. Servers that already joined stay.
Add many servers with a Fleet Token
A Fleet Token lets servers register under Waiting for you to review and enroll together. See Add many servers. You can also make a token from your computer:
| 1 | ployz server token create --expires 7d |
It prints the token once, and an install line for the release of the ployz CLI you ran. Run
that line on each server and paste the token when it asks:
| 1 | curl -fsSL https://ployz.sh | sudo sh -s -- VERSION --enroll |
Keep the version it names. Without one, the script installs the stable release, which may not
match your Ployz Cloud. With --json, the token is in fleet_token.token and the line in
install.
- Servers wait for you. A server that runs the line checks in under Waiting until you enroll it from Servers.
- It lasts 24 hours by default.
--expirestakes1d,7dor30d. - Expiry and revoke stop servers that haven't joined. Revoke drops waiting servers at once. After expiry, each waiting server is dropped when it next checks in. Enrollments already started finish, and servers already in stay.
- Only your login makes or revokes one. An organization token can't.
List your tokens, and revoke one by name:
| 1 | ployz server token ls |
| 2 | ployz server token revoke amber-falcon |
The Tokens page on Servers shows the same list. Revoking also drops the servers still waiting on that token. The prompt keeps the token out of shell history, sudo logs and the server's process list. See Install with a Fleet Token for cloud-init and rerun outcomes.
Use an empty spare disk for volumes
To put managed volumes on a spare disk, name it when you enroll the server:
| 1 | sudo ployz server add --storage zfs:/dev/disk/by-id/virtio-data |
A by-id path stays stable when the kernel renames disks after a reboot. You can also
name a kernel path such as zfs:/dev/vdb. The disk must
be an empty whole disk. Ployz refuses a disk with a filesystem, a partition table,
partitions, or mounts. It creates the ployz-device pool on the selected disk.
Retrying installation on the same disk also imports an exported pool before checking
whether the disk is empty. After a reboot, a volume lookup, mount, or storage request
imports that pool again. Ployz records device ownership before serving volumes,
including when the operating system already imported the pool. If the device is
unavailable, storage operations fail instead of creating a replacement pool on the
main disk.
With --storage zfs and no device, Ployz prepares ZFS and creates a file-backed pool
on the main disk when you deploy the first managed volume. A failed named-device
attempt that creates no pool leaves this option available. Existing file-backed
storage takes priority over pools found on attached disks. If a previously owned
device pool is missing, preparation refuses instead of forgetting it. For installer-only use,
ployzd install --storage zfs:/dev/vdb accepts the same device form.
Start without ZFS
The command sets up managed volumes with ZFS, so every volume gets a storage limit. ZFS needs:
- Ubuntu LTS with its stock kernel, Debian 12 or 13, or Amazon Linux 2023.
- Secure Boot off, on Debian and Amazon Linux.
- A VM or bare metal, not an OpenVZ or unprivileged LXC container.
- Free space on the main disk for your volumes.
On Ubuntu, ZFS comes ready-made. On Debian and Amazon Linux, the server builds it, so the command takes longer: 15–20 minutes on a 2-vCPU Amazon Linux Server, a few minutes on Debian. The build tools stay installed, so ZFS can be rebuilt when the kernel updates.
If your server can't run ZFS, put PLOYZ_SKIP_ZFS=1 after sudo in the install line, as in
curl -fsSL https://ployz.sh | sudo PLOYZ_SKIP_ZFS=1 sh -s -- VERSION --enroll. With
ployz server add, add --storage none.
The server then shows Docker only. If none of your servers has managed volumes, every new volume, a database's included, fails to deploy until you open it and tick Use a plain Docker volume (not recommended) under Advanced; see When your servers show Docker only.
Install with a Fleet Token
A Fleet Token starts with pflt_. Give it to the install script and the server asks to join
instead of joining. Use the version from the install line Add servers or
ployz server token create shows:
| 1 | curl -fsSL https://ployz.sh | sudo sh -s -- VERSION --enroll |
The script prints Installs Docker and Ployz. and asks for the
token at a Fleet Token (from Servers in Ployz Cloud): prompt that doesn't show what you paste. The token never appears on a
command line, so it stays out of sudo's log, shell history and ps. The script checks that the
token looks like one before it changes anything. It installs the CLI and runs
ployz server checkin --install, which checks in with the token and only once Cloud has answered
writes it to /etc/ployz/fleet-token, readable by root only. That ends with
Waiting for approval, within seconds: Docker installs and ZFS builds in the background while the
server waits, and the server shows on the Servers page at once. Two first installs end without
it and still exit cleanly, with the service set up and checking in:
- Cloud can't be reached. Connection errors, timeouts and server errors print the reason and
The check-in service keeps trying.The install writes the token without Cloud having answered it, and the server checks in every minute until Cloud does. - Cloud holds the check-in back. Cloud's own message follows
Cloud held this check-in back:, for exampleThis Fleet Token already has 5 waiting servers.The server isn't on the Servers page yet. It asks again when Cloud says to.
When Cloud already has an enrollment ready for the server, as Enroll on arrival can on a rerun, the
install ends with Cloud is ready to enroll this server. The check-in loop runs it. instead.
Without ZFS. Put
PLOYZ_SKIP_ZFS=1aftersudo, ascurl -fsSL https://ployz.sh | sudo PLOYZ_SKIP_ZFS=1 sh -s -- VERSION --enroll. It does what--storage nonedoes forployz server add.From user data. cloud-init runs user data as root with no terminal to prompt at, so pass the token in
PLOYZ_FLEET_TOKENinstead:yaml1 #cloud-config 2 runcmd: 3 - curl -fsSL https://ployz.sh | PLOYZ_FLEET_TOKEN=pflt_… sh -s -- VERSION Any process on the server, containers included, can read user data from the provider's metadata service for as long as the server runs. Give a token you put there a short expiry.
Run it again. The same line on the same server updates its token and keeps it one server. The server keeps
/etc/ployz/fleet-tokenuntil Cloud takes the new token, even when that file doesn't hold a token. If Cloud can't be reached, is busy, or the probe fails, a rerun with a different token ends withUntil Cloud takes the new Fleet Token, this install keeps /etc/ployz/fleet-token and changes nothing else. Run the install again.and an error. If the new token already has as many waiting servers as it allows, the rerun ends withCloud didn't take the new Fleet Token, so this install keeps /etc/ployz/fleet-token and changes nothing else.and an error. Two installs at once take turns writing the file, so a token Cloud didn't take never replaces one it took. A rerun with the same token keeps trying, as a first install does.A token file it can't read. If
/etc/ployz/fleet-tokenexists but can't be read, the install ends withCould not read /etc/ployz/fleet-tokenand an error before it writes anything. Fix or remove that file and run the install again.An expired token prints
Token expired., Cloud's reason, andRun the install again with a new token., and the script exits with an error. A revoked one says so the same way.A token Cloud doesn't know, or a missing
PLOYZ_CLOUD_URLwhen you run your own Cloud, ends the install withCloud at … refused this Fleet Token.and an error. It says to checkPLOYZ_CLOUD_URLonly when you set it.Something other than Ployz Cloud answers, such as a 404 or a web page from a wrong
PLOYZ_CLOUD_URL, a proxy or a firewall, the install ends with an error that names the URL it reached and says itisn't how Ployz Cloud answers. A proxy or firewall in between may have answered instead. It says to checkPLOYZ_CLOUD_URLonly when you set it.
When the install ends with an error because the token isn't shaped like one, Cloud refused it,
the clock is off, Cloud didn't answer or take a new token on a rerun, the token file couldn't be
read, or something other than Cloud answered, it has installed only the ployz CLI and started nothing. On a first install no
check-in service is set up and preparation never starts. On a server that already checks in, the
running service keeps its old token and goes on as before.
An error after Cloud has answered the token, such as systemd failing to start the service, leaves
a partial install. The new token is already in /etc/ployz/fleet-token, and the service files
and preparation may be set up. Fix the cause and run the install again.
Without --enroll, the script checks in only when PLOYZ_FLEET_TOKEN is set, and ends with
A Fleet Token is pflt_ and 43 more letters, digits, - or _ when it holds anything else, followed
by how long the value was, or, at the right length, that it has other characters. It never prints
the value. It leaves PLOYZ_ENROLL_TOKEN to
ployz server add, and refuses a Fleet Token in it, which older install lines put there; use
PLOYZ_FLEET_TOKEN instead. If the server
waits longer than you expect, see
A server waits for approval.
Open the firewall
Ployz opens the ports it needs on the server itself. If your provider runs a firewall in front of it, like a Hetzner Cloud Firewall, a DigitalOcean Cloud Firewall or an AWS security group, allow these there:
| Port | From | Needed for |
|---|---|---|
| TCP 80, TCP and UDP 443 | Anywhere | Your apps over https, and their certificates, on servers that take web traffic |
| UDP 51820 | Your other servers | Services on different servers reaching each other |
Good to know
- Operating system updates stay with you. Ployz doesn't install them.
- Already running Docker? Ployz keeps it and its settings, if it's Docker 29.2 or newer with buildx 0.18 or newer and the containerd image store turned on. Otherwise setup stops before changing anything; see The install stops while checking Docker. Docker's default never rotates container logs, so they can fill the disk; see The server's disk is filling up.
- Pinning your own Caddy?
--ingress-imageneeds Caddy 2.8 or newer. Ployz refuses an older version tag. - A server that never joined doesn't hold up the next one. If a token was used but its server
never joined,
ployz server lslists it as down with no version. The nextployz server adddoesn't wait on it: it warnsServer web-3 shows no sign it ever joinedand printsployz server forget <its ID> --confirm deadto take it out. With--json, the result lists each such server underunarrived, with itsid,nameand that command asnext. You get the warning, the command and the list even when the add then fails for another reason, such as a down server. - Client IPs stay out of Ployz's web server logs, not your app's. Caddy passes the client IP to
your app in
X-Forwarded-For, over IPv4 and IPv6. If your app logs requests, its logs, and the Log Store's copy of them, can hold client IPs and user agents. Those logs are yours to manage.
Next: manage your servers.