The documentation here is for an unreleased version of Recyclarr.
Visit the documentation site for the Current Version instead.Client and Server
Recyclarr has two programs:
- The server (
recyclarr-server) does the work. It reads your configuration, talks to Sonarr and Radarr, runs syncs, and sends notifications. - The CLI (
recyclarr) is a client. Each command sends a request to a server and shows the result. Other programs can send the same requests through the HTTP API.
The server reads your YAML configuration and settings.yml when it starts. If either is not valid,
the server does not start. Restart the server after you change them.
A persistent server also updates guide data while it runs. When an update brings new guide data, the server reads your YAML configuration files again, so an edit you have not restarted for can take effect at that point. Restart the server to apply your edits right away.
Persistent Server
A persistent server keeps running until you stop it. The Docker image runs one, and
recyclarr serve starts one on your own machine.
A persistent server:
- Syncs on its built-in schedule.
- Listens for requests at the address in its
serversettings. - Writes its log to the console and to log files in
logs/server/in the data directory. - Serves health probes and metrics on a separate management port.
- Keeps its sync jobs in
server.dbin the state directory.
A persistent server needs an API key and does not start without one.
For CLI commands to use a persistent server, set server.base_url in cli.yml or set
the RECYCLARR_SERVER_URL environment variable. The Docker image sets the
variable for you.
Temporary Server
If no server address is set, each CLI command starts its own temporary server. The temporary server
listens only on the local machine, handles that one command, and stops when the command ends. It
does not run the built-in schedule, opens no management port, and does not keep its jobs after it
stops. It writes log files to logs/server/ in the data directory and sends
notifications. It downloads guide data only when its cached copy is out of date,
so most commands start without network access.
This is the default when you run recyclarr outside Docker without any extra setup. You do not need
to set an API key: the CLI creates a new one-time key for each command and gives it to the temporary
server.
Server API Key
Every request to the server's API (any path under /api) must include the server API key. This
keeps other programs on your network, or other users on your machine, from starting syncs or
deleting custom formats in your Sonarr and Radarr instances. The API reference
pages need no key. Health probes and metrics need no key either, but they are on the separate
management port, not the API port.
Setting the Key
The server reads its key from one of two environment variables, never from settings.yml:
RECYCLARR_API_KEYholds the key itself.RECYCLARR_API_KEY_FILEholds the path of a file that contains the key, such as a Docker Compose secret or a Kubernetes Secret volume.
Set only one of them. The server logs a fatal error and exits with a non-zero exit code when neither
is set, when both are set, when it cannot read the key file, or when the key is shorter than 32
characters. This applies to recyclarr serve, the Docker image, and any
other way you run a persistent server.
Use a long random value, for example:
openssl rand -hex 32
The server reads the key file again on every request, so you can rotate the key by replacing the file's contents without a restart. If the file later becomes unreadable or holds a key that is too short, the server rejects every request and logs the cause.
Sending the Key
-
The CLI sends the key from
server.api_keyincli.yml, or fromRECYCLARR_API_KEYorRECYCLARR_API_KEY_FILEwhen either is set. Inside the Docker container, the CLI reads the same variables as the server, sodocker execcommands need no extra setup. -
Other HTTP clients send the key as a bearer token in the
Authorizationheader:curl -H "Authorization: Bearer $RECYCLARR_API_KEY" http://localhost:7982/api/v1/sync/jobs
When the key is missing or wrong, the server responds with 401 Unauthorized, a
WWW-Authenticate: Bearer header, and a Problem Details body. The CLI shows this message:
The Recyclarr server rejected the request: missing or wrong API key. Set server.api_key in cli.yml, or the RECYCLARR_API_KEY or RECYCLARR_API_KEY_FILE environment variable, to the key the server uses.
Kubernetes
Store the key in a Secret, created by hand or by a tool such as External Secrets Operator, and map
it to RECYCLARR_API_KEY in the container:
containers:
- name: recyclarr
image: ghcr.io/recyclarr/recyclarr:9
env:
- name: RECYCLARR_API_KEY
valueFrom:
secretKeyRef:
name: recyclarr
key: api-key
To rotate the key without restarting the pod, mount the Secret as a volume instead and point
RECYCLARR_API_KEY_FILE at the file. Kubernetes updates the mounted file when the Secret changes
(this does not work with subPath mounts):
containers:
- name: recyclarr
image: ghcr.io/recyclarr/recyclarr:9
env:
- name: RECYCLARR_API_KEY_FILE
value: /run/secrets/recyclarr/api-key
volumeMounts:
- name: api-key
mountPath: /run/secrets/recyclarr
readOnly: true
volumes:
- name: api-key
secret:
secretName: recyclarr
For ports, health probes, and telemetry, see the Kubernetes example.
Network Security
The server serves plain HTTP and has no TLS settings. The API key travels unencrypted between a client and the server, and the management port needs no key at all. Decide which clients can reach each port, and add TLS where traffic crosses a network you do not trust.
- TLS: Terminate TLS in front of the server with a reverse proxy, a tunnel, a Kubernetes Ingress, or a service mesh. Plain HTTP between that proxy and the server is fine on a network you trust.
- API port: Publish it only to the clients that need it. CLI commands run with
docker execreach the server inside the container, so they need no published port at all. - Management port: It listens on the same address as the API port. In Docker, publish it only if
something outside the container scrapes metrics or probes health. In Kubernetes, probes and
Prometheus reach it on the pod IP, so restrict it with a
NetworkPolicy.
To publish the API port only on the Docker host itself, bind it to the loopback address:
services:
recyclarr:
ports:
- 127.0.0.1:7982:7982
This NetworkPolicy lets only pods in a monitoring namespace reach the management port, while the
API port stays open to the rest of the cluster. Health probes still work, because a NetworkPolicy
always allows traffic from the pod's own node, where the kubelet runs. Adjust the selectors to your
cluster:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: recyclarr
spec:
podSelector:
matchLabels:
app: recyclarr
policyTypes:
- Ingress
ingress:
- ports:
- port: 7982
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- port: 7983
API Reference
Each server describes its own API on the same address and port as the API itself, so the description always matches the version you run:
/scalaris an interactive reference page where you can browse operations and try requests./openapi/v1.jsonis the OpenAPI document, for tools that generate clients or import an API.
Both paths are open without the API key. Requests you send from the /scalar page go to /api, so
enter the key there before you try them. This site also has the reference under HTTP API.
Sync Jobs
Every sync runs as a job, whether the schedule, the CLI, or the API started it. The server runs one job at a time, oldest first. Other jobs wait in a queue.
Each job has one of these statuses:
| Status | Meaning |
|---|---|
pending | Waiting in the queue |
running | Syncing now |
succeeded | The sync finished without errors |
partial | The sync finished, but some changes failed |
failed | The sync failed |
interrupted | The server stopped while the job was pending or running |
skipped | A scheduled sync was not started because another job was already active |
A persistent server keeps the 50 most recent finished jobs and their results, including across
restarts. When the server starts, it marks jobs that were active at shutdown as interrupted. It
does not run them again.