Installation
Roosty provides an Ansible deployment for a single Debian or Ubuntu host. It installs a Docker Compose stack containing Roosty, PostgreSQL, and Caddy, with persistent database and media storage. Caddy obtains and renews the public TLS certificate.
Roosty is early-stage software. Review the compatibility matrix before operating a public instance.
Prerequisites
Prepare:
-
A Debian or Ubuntu host reachable through SSH.
-
A public domain with A and/or AAAA records pointing to the host.
-
Inbound TCP ports 80 and 443.
-
Ansible on the machine from which you will deploy.
-
An SSH deployment account with passwordless or interactive
sudoaccess.
If you enable the bundled Elk or Phanpy client, create DNS records for its subdomain as well.
Configure DNS
Choose the instance domain before creating accounts or enabling federation. The domain becomes part of account handles, actor URLs, status URLs, and other federated identifiers. Changing it later is a migration rather than a routine configuration update.
Create an A record for IPv4 and, only when the host has working public IPv6 connectivity, an AAAA record for IPv6.
Both record types must point to the host running Caddy.
Do not publish a stale or unreachable AAAA record: certificate validation, clients, and remote servers may prefer IPv6 even when IPv4 works.
For example, an installation at social.example.com with both bundled clients could use:
| Name | Type | Value |
|---|---|---|
|
|
The server’s public IPv4 address. |
|
|
The server’s public IPv6 address, when IPv6 is fully configured. |
|
|
The same server addresses, or a CNAME to |
|
|
The same server addresses, or a CNAME to |
Replace these names and addresses with your real values.
The elk. and phanpy. names are not required when their corresponding clients are disabled.
A wildcard record is also unnecessary.
Roosty does not require special SRV or TXT records for ActivityPub discovery.
WebFinger and ActivityPub are served over HTTPS from roosty_domain.
Existing mail-related MX and TXT records can remain unchanged.
If the domain uses CAA records, ensure they permit the certificate authority used by Caddy. If a DNS proxy or CDN is enabled, it must forward ports 80 and 443 to this host and support WebSockets and normal API request bodies. Using direct DNS records is the simplest initial setup.
Create the records before running the playbook so Caddy can complete ACME validation. DNS changes can remain cached until their previous TTL expires. Check the authoritative result from a machine outside the server’s network:
dig +short A social.example.com
dig +short AAAA social.example.com
dig +short A elk.social.example.com
dig +short A phanpy.social.example.com
Every returned address must be public, belong to the intended server or proxy, and accept inbound HTTP and HTTPS traffic. An empty result for an optional client or for unused IPv6 is valid.
Configure the inventory
The example production inventory is under deploy/ansible/inventories/production.
Replace the example host and user in hosts.yml, then configure the instance in group_vars/roosty.yml.
At minimum, set:
-
roosty_domainto the public instance domain. -
roosty_acme_emailto the certificate contact address. -
roosty_image_tagto the Roosty release to deploy. -
roosty_instance_nameandroosty_instance_descriptionfor users and clients.
Registration is closed by default. Federation is also disabled until its domain policy and encryption secret are configured. Keep those defaults for the first deployment.
Ansible parameters
Set deployment parameters in deploy/ansible/inventories/production/group_vars/roosty.yml.
Parameters marked Required have no role default and must be supplied by the inventory.
Instance and deployment
| Parameter | Default | Purpose |
|---|---|---|
|
Required |
Public instance domain, without a scheme or path. |
|
Required |
Contact address used by Caddy for ACME certificates. |
|
Required |
Roosty container tag from |
|
Required |
Instance name exposed to users and clients. |
|
Required |
Instance description exposed to users and clients. |
|
Required |
Registration mode: |
|
|
Host directory containing generated Compose, Caddy, environment, and secret files. |
|
|
APT packages installed on the target host. |
Bundled clients
| Parameter | Default | Purpose |
|---|---|---|
|
|
Deploy Elk at |
|
|
Container UID that owns Elk’s persistent application storage. |
|
|
Serve Phanpy at |
|
Role-pinned build |
Phanpy release embedded in the Caddy image. |
Federation and workers
| Parameter | Default | Purpose |
|---|---|---|
|
Required |
Enable ActivityPub discovery and delivery. |
|
Required |
YAML list of exact domains allowed for federation. The single entry |
|
Required |
Maximum age for retrying failed federation deliveries, as a non-zero human-readable duration. |
|
|
Durable worker loops per Roosty process. |
|
|
Retention period for successfully completed durable jobs. |
|
|
Retention period for permanently failed jobs and their diagnostics. |
Scheduled statuses and streaming
| Parameter | Default | Purpose |
|---|---|---|
|
|
Minimum time between scheduling and publication. |
|
|
Maximum pending scheduled statuses per account. |
|
|
Maximum scheduled publications per account and UTC day. |
|
|
Maximum simultaneous streaming sockets per process. |
|
|
Maximum time allowed to send one WebSocket frame. |
|
|
Interval between server WebSocket pings. |
|
|
Maximum time without an inbound frame; must exceed the ping interval. |
|
|
Retention period for the PostgreSQL streaming recovery log. |
Environment variables
The Ansible deployment writes ordinary settings to <roosty_deploy_dir>/.env and generated secrets to <roosty_deploy_dir>/secrets.env.
Do not edit those generated files directly; set the corresponding Ansible parameter and rerun the playbook.
Secrets are generated once and retained across deployments.
The following variables are the complete server configuration interface for manual deployments.
Durations accept values such as 1500ms, 10s, 5m, 1h, or 30d.
Core server
| Variable | Default | Purpose |
|---|---|---|
|
Required |
PostgreSQL connection URL. |
|
Required |
Externally visible base URL. Federation requires an absolute HTTPS URL. |
|
|
Application HTTP listener. |
|
Unset |
Optional listener for |
|
Required |
At least 32 bytes; signs browser sessions and protects stored Web Push credentials. Use the same value for every process sharing the database. |
|
Required |
At least 32 bytes; hashes OAuth tokens and client secrets. Use the same value for every process sharing the database. |
|
Required |
Instance name exposed to users and clients. |
|
Unset |
Optional instance description. |
|
|
Registration mode: |
|
|
Maximum accepted signup attempts from one client during the burst window. |
|
|
Short rolling signup-attempt window. |
|
|
Maximum accepted signup attempts from one client during the longer window. |
|
|
Long rolling signup-attempt window; it must exceed the burst window. |
|
|
IPv6 prefix length used to group signup clients, from 1 through 128. IPv4 addresses remain exact. |
|
Empty |
Comma-separated proxy CIDRs permitted to supply |
|
|
Advertise eligible public profiles and statuses to search engines. |
|
Build-dependent |
Directory containing the packaged first-party web assets. |
Registration attempts are durably limited across all processes. Invalid signup fields and duplicate
accounts consume capacity; closed registration and rejected app credentials or scopes do not. If open
registration is served through a reverse proxy, including the Ansible Caddy deployment, explicitly set
ROOSTY_TRUSTED_PROXY_CIDRS (or roosty_trusted_proxy_cidrs) to the actual container-network CIDR.
Roosty never implicitly trusts private networks or forwarded headers.
When search indexing is disabled, every public profile and status document emits
noindex, nofollow, JSON-LD is omitted, sitemap routes return 404, and robots.txt no longer
advertises a sitemap. Crawling remains allowed so engines can observe noindex and remove old
results. Use the same setting on every Roosty process behind a shared hostname.
Media and Web Push
| Variable | Default | Purpose |
|---|---|---|
|
|
Media storage backend. |
|
|
Directory for locally managed media. |
|
|
Retention period for successfully fetched remote media. |
|
|
Maximum accepted size of one remote media response. |
|
|
Maximum concurrent remote media downloads per process, shared by durable workers and cold-cache HTTP requests; must be positive. |
|
|
Maximum concurrent preview-card downloads per process; must be positive. PostgreSQL additionally serializes and rate-limits requests to each remote host across all processes. |
|
Unset |
Base64-encoded PKCS#8 P-256 private key for Web Push. All processes sharing the database must use the same key. |
Federation and workers
| Variable | Default | Purpose |
|---|---|---|
|
|
Enable ActivityPub federation. |
|
Required when enabled |
At least 32 bytes; encrypts local actor private keys. It must remain identical across processes and restarts. |
|
Required when enabled |
Comma-separated exact domains. |
|
|
Maximum age for retrying a failed delivery. |
|
|
Age at which active local RSA and Ed25519 actor keys rotate. Must be positive. |
|
|
How long retiring actor keys remain published and usable. Must be positive and shorter than the rotation interval. |
|
|
Durable worker loops per process. |
|
|
How long successfully completed durable jobs remain in PostgreSQL. Must be a non-zero humantime duration. |
|
|
How long permanently failed jobs and their diagnostics remain in PostgreSQL. Must be a non-zero humantime duration. |
|
|
Trend scoring cadence. Must be at least |
|
|
Per-process cadence for refreshing the global account-suggestion materialized view. Accepts a non-zero humantime duration. Changes take effect when the worker process restarts; processes may use different values safely. |
When multiple Roosty processes serve media, ROOSTY_MEDIA_ROOT must refer to the same shared
filesystem on every API and worker process. This includes cached preview-card thumbnails. The
currently supported local backend does not replicate files between node-local filesystems.
Scheduled statuses and streaming
| Variable | Default | Purpose |
|---|---|---|
|
|
Minimum time between scheduling and publication. |
|
|
Maximum pending scheduled statuses per account; must be positive. |
|
|
Maximum scheduled publications per account and UTC day; must be positive. |
|
|
Maximum simultaneous streaming sockets per process; must be positive. |
|
|
Maximum duration of one outbound WebSocket send. |
|
|
Interval between server WebSocket pings. |
|
|
Maximum time without an inbound frame; must exceed the ping interval. |
|
|
Retention period for the PostgreSQL streaming recovery log. |
The production Compose definition also consumes deployment-only variables such as ROOSTY_IMAGE_TAG, ROOSTY_PHANPY_VERSION, ROOSTY_DOMAIN, and ROOSTY_ACME_EMAIL.
These select container and proxy behavior and are not read by the Roosty server itself.
Deploy
From the repository:
cd deploy/ansible
ansible-playbook site.yml
The playbook installs the required host packages, writes the Compose and Caddy configuration, creates persistent secrets, starts the services, and waits for Roosty to become ready. Re-run the same command to apply configuration or image-version changes.
Create the first account
Bootstrap an administrator on the deployed host:
sudo docker compose -f /opt/roosty/compose.yaml exec roosty \
/usr/local/bin/roosty admin bootstrap \
--username admin \
--email admin@example.com
The command prints the initial password. Store it securely and change it after signing in.
Verify the instance
Check the public instance and its infrastructure endpoints:
curl --fail https://your-domain.example/api/v2/instance
sudo docker compose -f /opt/roosty/compose.yaml exec -T roosty \
curl --fail http://127.0.0.1:3001/healthz
sudo docker compose -f /opt/roosty/compose.yaml exec -T roosty \
curl --fail http://127.0.0.1:3001/readyz
The production Compose stack does not publish the infrastructure listener on the host.
For client options and first sign-in, continue with Using Roosty. For account provisioning and day-to-day operations, see Administration. For federation policy, operational configuration, upgrades, and release behavior, see the contributor and deployment guide.