To self-host Paperclip, run its official Docker Compose stack on a Linux VPS: one container for the Paperclip server and one for PostgreSQL, with the web interface on port 3100 behind a login. Paperclip is the open-source "control plane" that runs a team of AI agents like a small company, with an org chart, tasks, budgets and an audit log. The control plane needs no graphics card. In our lab on 15 October 2026 it ran on 2 CPU cores and used about 1 GB of RAM with Postgres included. The expensive part is usually the model behind each agent, such as Claude or a Codex model, billed by the provider. We also ran a small agent on CPU alone with a local model, and it finished a task in 5 to 8 seconds.
Key facts, checked on 15 October 2026:
- Paperclip is MIT-licensed and had about 102k stars on GitHub. The current stable release is v2026.1012.0, published on 12 October 2026 (release notes in the repository's
releasesfolder). - A source install needs Node.js 24.11 or newer and pnpm 9.15 or newer, per the official README. With Docker you need neither.
- The repository's
docker/docker-compose.ymlpairs the server withpostgres:17-alpineand starts it in "authenticated" mode, so nobody gets in without an account. - The prebuilt image
ghcr.io/paperclipai/paperclip:latestis the stable channel (perdoc/CHANNELS.md). It was 6.99 GB on disk in our lab and took 4 minutes 2 seconds to pull. - Building the image from source on 2 cores and 3 GB of RAM did not finish: after 28 minutes the TypeScript step was using 2.6 GB and free memory was gone. It also left 11.79 GB of build cache behind.
What runs on your server, and what you pay for elsewhere
Paperclip itself is the office, not the workers. It stores companies, agents, tasks, comments and approvals in PostgreSQL. It also wakes agents (a "heartbeat", started by a new task, a schedule or a click) and records what each one did. The agents are separate programs, connected through adapters for Claude Code, Codex, Cursor, Gemini CLI, OpenCode, plain shell commands ("process") and HTTP webhooks.
| Part | Where it runs | What it costs you |
|---|---|---|
| Paperclip server and web UI | Your VPS, in Docker | Server resources only (MIT licence) |
| PostgreSQL 17 | Your VPS, second container | Server resources only |
| Claude Code, Codex and similar agents | The agent program runs on your VPS, but the model runs in the provider's cloud | A subscription or per-token API billing from Anthropic, OpenAI and others |
| Small local model (optional) | Your VPS, on CPU through Ollama | Server resources only, but much weaker answers |
The server bill stays flat; the model bill grows with the work. If you only want one coding agent running all day without the company layer, our guide to running Claude Code and Codex 24/7 on a VPS is the simpler setup.
What we measured in our lab
We tested on a Debian 13 container limited to 2 CPU cores and 3 GB of RAM. It ran on an AMD EPYC 7742 host whose 8 cores were shared with other tests at the same time, so a real server can be faster or slower. All numbers come from our lab.
| Measurement | Result |
|---|---|
| First start to a healthy API (includes 304 database migrations) | 34 seconds |
| Restart of the server container | 18 seconds |
| RAM at idle, Paperclip server | 0.89 to 1.01 GB |
| RAM at idle, PostgreSQL | 100 to 150 MB |
| Disk: images / database / app data after the tests | 7.3 GB / 79 MB / 20 MB |
| One agent task with a local 0.5B model, assignment to "done" | 5 to 8 seconds per run |
| Local model speed, qwen2.5 0.5B / 1.5B, 2 threads | 24 to 27 / about 13 tokens per second |
| pg_dump of the whole database | 0.9 seconds, 1.3 MB file |
During an agent run, Ollama took 1.6 to 1.8 of our 2 cores for a few seconds, while the Paperclip server used 6 to 28 percent of one core and PostgreSQL up to 42 percent. The control plane is light; local thinking is what eats CPU.
Step 1: prepare Debian 13 and a normal user
Start from a fresh server and secure SSH first (our Debian 13 hardening guide covers it). Then install Docker and create a user for Paperclip:
# as root
apt update
apt install -y git curl ca-certificates docker.io docker-compose sudo
useradd -m -s /bin/bash -G sudo,docker clip
docker --version
docker compose version
We got Docker version 26.1.5+dfsg1 and Docker Compose version 2.26.1-4 from Debian's own packages. Give the new user a password or your SSH key so you can log in as it. Membership of the docker group is close to root access. More background is in our Docker on a VPS guide.
Step 2: get the official compose files and a secret
Log in as the new user. Clone the repository and create the one secret the compose file insists on:
# as the clip user
git clone --depth 1 https://github.com/paperclipai/paperclip.git
cd paperclip/docker
echo BETTER_AUTH_SECRET=$(openssl rand -hex 32) > .env
echo PAPERCLIP_PUBLIC_URL=http://localhost:3100 >> .env
BETTER_AUTH_SECRET signs login sessions. The compose file refuses to start without it.
Step 3: fix two defaults before the first start
Read docker-compose.yml before you run it. On a server with a public IP, two lines in it matter:
- The database publishes
"5432:5432"with the user and password both set topaperclip. That puts PostgreSQL on every network interface with a password anyone can guess. - The app publishes
"3100:3100", so the login page is open to the internet.
Docker writes its own firewall rules, so published ports usually bypass ufw. We left the official file alone and added an override next to it, which also switches to the prebuilt image (building from source failed in 3 GB of RAM):
# as the clip user, in ~/paperclip/docker
cat > compose.override.yml <<'EOF'
services:
server:
image: ghcr.io/paperclipai/paperclip:latest
pull_policy: always
ports: !override
- "127.0.0.1:3100:3100"
db:
ports: !reset []
EOF
Postgres is now reachable only over Docker's internal network, and the web UI only on the server itself. Your Paperclip data lives in two Docker volumes: pgdata holds the database and paperclip-data holds uploads, keys and agent workspaces.
Step 4: start Paperclip and check it
# as the clip user, in ~/paperclip/docker
docker compose -f docker-compose.yml -f compose.override.yml pull
docker compose -f docker-compose.yml -f compose.override.yml up -d
curl -s http://127.0.0.1:3100/api/health
The first curl attempts may say "Connection reset by peer" while migrations run. In our lab the answer arrived after 34 seconds and began like this:
# output seen by the clip user
{"status":"ok","deploymentMode":"authenticated","deploymentExposure":"private",
...,"bootstrapStatus":"bootstrap_pending", ...}
docker compose ps should show the database as Up (healthy) with 5432/tcp and no host address, and the server on 127.0.0.1:3100->3100/tcp. bootstrap_pending means nobody owns the instance yet.
Step 5: claim the admin account, then close sign-up
In authenticated and private mode, the first person who creates an account and claims the instance becomes its administrator. Do it before anyone else can reach the login page. The docs describe a browser flow (create an account, then use the setup screen). Because our UI listens on 127.0.0.1 only, you reach it through an SSH tunnel from your own computer (the -L option of ssh) or through a private network such as Tailscale. We did the same two steps from the server's terminal:
# as the clip user
B=http://localhost:3100
curl -s -c ~/cj.txt -H "Origin: $B" -H "Content-Type: application/json" \
-X POST $B/api/auth/sign-up/email \
-d '{"email":"you@example.com","password":"A-LONG-RANDOM-PASSWORD","name":"Admin"}'
curl -s -b ~/cj.txt -c ~/cj.txt -H "Origin: $B" -H "Content-Type: application/json" \
-X POST $B/api/bootstrap/claim -d '{}'
The second call answered {"claimed":true,...}. Then we tried something most guides skip. A second sign-up after the claim still worked and created an account. That account saw an empty company list and got "User does not have access to this company", so it could not read anything. It is still a login nobody should have. The setting PAPERCLIP_AUTH_DISABLE_SIGN_UP, documented in the repository's AWS guide, closes registration. Add it to the server: section of compose.override.yml, right under pull_policy: always:
# as the clip user: lines added to the server section of compose.override.yml
environment:
PAPERCLIP_AUTH_DISABLE_SIGN_UP: "true"
# as the clip user, in ~/paperclip/docker
docker compose -f docker-compose.yml -f compose.override.yml up -d
After the restart, sign-up answered "Email and password sign up is not enabled", and the admin session kept working. Invite colleagues from inside Paperclip from then on.
Step 6: a first company and an agent that runs on CPU
We could not log in to paid model services in the lab, so we did not run Claude Code or Codex agents. Instead we built the cheapest kind of employee Paperclip supports: a process agent, which is just a command that Paperclip starts with a short-lived API key in its environment. Ours asks a small local model for an answer and posts it to the task. First, add Ollama to the same override file under services::
# as the clip user: lines added to compose.override.yml
ollama:
image: ollama/ollama:latest
volumes:
- ollama:/root/.ollama
volumes:
ollama:
# as the clip user, in ~/paperclip/docker
docker compose -f docker-compose.yml -f compose.override.yml up -d
docker exec docker-ollama-1 ollama pull qwen2.5:0.5b
This pulled Ollama 0.40.4, and its image alone is 5.57 GB. One trap is easy to miss. Inside our container, which was capped at 2 cores, Ollama started 8 threads and crawled at 0.13 tokens per second. With the option num_thread set to 2, the same 1.5B model ran at about 13 tokens per second, and the 0.5B model at 24 to 27. Set num_thread to the number of vCPUs you actually have.
The agent script lives in the Paperclip data volume. It reads its own identity and lists its open tasks. Then it claims each task with a "checkout", gets the model's answer, and marks the task done with a comment:
# as the clip user: ~/local-writer.py
#!/usr/bin/env python3
import json, os, sys, urllib.request, urllib.error
API = os.environ["PAPERCLIP_API_URL"].rstrip("/")
KEY = os.environ["PAPERCLIP_API_KEY"]
RUN = os.environ.get("PAPERCLIP_RUN_ID", "")
OLLAMA = os.environ.get("OLLAMA_URL", "http://ollama:11434")
MODEL = os.environ.get("OLLAMA_MODEL", "qwen2.5:0.5b")
THREADS = int(os.environ.get("OLLAMA_THREADS", "2"))
def call(method, url, body=None, auth=True):
req = urllib.request.Request(url, method=method,
data=json.dumps(body).encode() if body is not None else None)
req.add_header("Content-Type", "application/json")
if auth:
req.add_header("Authorization", "Bearer " + KEY)
req.add_header("X-Paperclip-Run-Id", RUN)
try:
with urllib.request.urlopen(req, timeout=600) as r:
return json.loads(r.read() or b"{}")
except urllib.error.HTTPError as e:
sys.exit(f"{method} {url} -> {e.code}: {e.read()[:300]!r}")
me = call("GET", API + "/api/agents/me")
tasks = call("GET", f"{API}/api/companies/{me['companyId']}/issues"
f"?assigneeAgentId={me['id']}&status=todo,in_progress")
tasks = tasks if isinstance(tasks, list) else tasks.get("items", [])
print(f"{me['name']}: {len(tasks)} open task(s)")
for t in tasks:
call("POST", f"{API}/api/issues/{t['id']}/checkout",
{"agentId": me["id"], "expectedStatuses": ["todo", "backlog", "blocked", "in_progress"]})
prompt = f"{t['title']}\n\n{t.get('description') or ''}\nAnswer in under 120 words."
out = call("POST", OLLAMA + "/api/generate", {"model": MODEL, "prompt": prompt,
"stream": False, "options": {"num_thread": THREADS, "num_predict": 200}}, auth=False)
tps = out["eval_count"] / (out["eval_duration"] / 1e9)
print(f"{t['identifier']}: {out['eval_count']} tokens at {tps:.1f} tok/s")
call("PATCH", f"{API}/api/issues/{t['id']}", {"status": "done",
"comment": out["response"] + f"\n\n({MODEL} on CPU, {tps:.1f} tokens/s)"})
# as the clip user
docker exec docker-server-1 mkdir -p /paperclip/agents
docker cp ~/local-writer.py docker-server-1:/paperclip/agents/
We created the company and the agent through the API with the admin session from Step 5. The web UI asks for the same fields, but we did not click through it. The part that matters is the adapter configuration:
# as the clip user (set CID to the "id" from the first answer)
curl -s -b ~/cj.txt -H "Origin: $B" -H "Content-Type: application/json" \
-X POST $B/api/companies -d '{"name":"Lab Co"}'
curl -s -b ~/cj.txt -H "Origin: $B" -H "Content-Type: application/json" \
-X POST $B/api/companies/$CID/agents -d '{"name":"Writer","role":"general",
"title":"Local Writer","adapterType":"process","adapterConfig":{"command":"python3",
"args":["/paperclip/agents/local-writer.py"],"cwd":"/paperclip/agents","timeoutSec":600,
"env":{"OLLAMA_MODEL":"qwen2.5:0.5b","OLLAMA_THREADS":"2"}}}'
Two lessons. First, put the program in command and its arguments in args. Our first try with the whole line in command failed with "Failed to start command". Second, give the agent work by assigning a task. A manual heartbeat has no task attached, so our update was refused with "Cross-issue writes need a run to attribute them to". Once we created a task assigned to the Writer, Paperclip woke the agent by itself. Ten seconds later the task was done, with the model's answer as a comment, signed "qwen2.5:0.5b on CPU, 24.3 tokens/s". The activity log recorded issue.checked_out, issue.updated and issue.comment_added by the agent.
A 0.5B model writes fluent text that is often generic. Use local models for sorting and short drafts, and cloud models for coding. Models bigger than about 3B parameters get slow on CPU, and anything at the size of the cloud models needs a GPU, which our servers do not have. Our guide to AI apps and LLMs on a VPS covers what CPU-only models can do.
Secrets, budgets and who may approve spending
From Paperclip's documentation (not tested with real money):
- Secrets such as API keys are encrypted at rest with a local master key under
instances/default/secrets/in the data volume. They are decrypted only when a run starts. The docs warn that a secret bound to an agent counts as exposed to that agent, so bind only what each one needs. - Budgets are set in cents per month, for the company and per agent. At 80 percent the agent gets a soft warning. At 100 percent it is paused, with no more heartbeats until you raise the budget or the month ends (costs and budgets guide). Our local agent reported 0 cents of spend, as expected.
- The human "board" approves the CEO agent's strategy and new hires (hire approvals can be switched on), and can override budgets. Whoever holds the admin login controls the money.
- Telemetry is on by default. The README says
PAPERCLIP_TELEMETRY_DISABLED=1orDO_NOT_TRACK=1turns it off.
Set a spend cap at the model provider too. Paperclip can only count what agents report.
Backups are your job: pg_dump plus the data volume
The server log says Paperclip makes its own database backups every 60 minutes and keeps them 7 days, but it writes them into the same data volume on the same disk. That helps after a bad upgrade, not after losing the server. Make your own dump and copy it somewhere else:
# as the clip user, in ~/paperclip/docker
mkdir -p ~/backups
docker compose exec -T db pg_dump -U paperclip -d paperclip -Fc > ~/backups/paperclip-$(date +%F).dump
ls -lh ~/backups
Our dump took 0.9 seconds and was 1.3 MB. A backup only counts once you have restored it, so we restored it into a scratch database and counted rows:
# as the clip user, in ~/paperclip/docker
docker compose exec -T db createdb -U paperclip restore_test
docker compose exec -T db pg_restore -U paperclip -d restore_test --no-owner < ~/backups/paperclip-$(date +%F).dump
docker compose exec -T db psql -U paperclip -d restore_test -tAc "select count(*) from agents;"
docker compose exec -T db dropdb -U paperclip restore_test
The restore ran without errors and returned the rows we expected. The docs add one detail: back up the secrets key together with the database, because a database backup without the key cannot decrypt stored secrets. Copy the paperclip-data volume as well. To ship both off the server on a schedule, see our offsite backups guide with restic. For PostgreSQL care, see databases on a VPS.
HTTPS when you want it on a domain
Keep port 3100 on 127.0.0.1 and put a reverse proxy in front of it. Caddy, Nginx or Traefik then holds the certificate and forwards to 127.0.0.1:3100. Our reverse proxy and SSL guide has the configs. On the Paperclip side, the docs ask for PAPERCLIP_PUBLIC_URL set to the real https:// address and PAPERCLIP_DEPLOYMENT_EXPOSURE set to public for an internet-facing instance. Some newer features, such as letting outside assistants connect over MCP, require an HTTPS public URL. We did not test this part, because a lab has no real domain. For a team-only tool, a private network is simpler.
Which plan and which city
RS Computers runs KVM servers in Amsterdam, Dublin and Prishtina. VPS plans have shared vCPUs, which suits an app that sits idle most of the time. VDS plans have dedicated vCPUs, which suit agents and local models that keep the CPU busy.
| Your setup | Plan | Why |
|---|---|---|
| Paperclip plus Postgres, agents are cloud models or HTTP and process agents | VPS Mini: 4 vCPU (shared), 4 GB RAM, 80 GB NVMe | About 1.1 GB used at idle in our lab. That leaves room for Docker, a reverse proxy and the 7 GB image. |
| Claude Code or Codex agents running inside the Paperclip container | VDS Small: 4 dedicated vCPU, 8 GB RAM, 240 GB NVMe | Each agent CLI is a separate process with its own memory (Claude Code's docs ask for 4 GB), and test runs need real CPU. |
| Several agents at once plus a small local model | VDS Medium: 8 dedicated vCPU, 16 GB RAM, 480 GB NVMe | Ollama with a 1.5B model took 1.7 GB in our lab and keeps every thread it gets busy. Dedicated cores keep the UI responsive. |
| Many agents building and testing code all day | VDS Large: 16 dedicated vCPU, 32 GB RAM, 960 GB NVMe | Parallel builds and test suites, not Paperclip itself, decide the size. |
You can move to a bigger plan later from the client area with a short reboot. For the city, pick the one near the people using the dashboard; agents mostly talk to model APIs anyway. In our own ping.pe test on 6 October 2026, our Amsterdam server answered in 6.6 ms from Frankfurt and 6.7 ms from London. Our Prishtina server answered in 18.6 ms from Vienna and 27.8 ms from Frankfurt. Dublin suits teams in Ireland and the UK. Availability by city is on the plans page.
If nobody on your team wants to own Docker, upgrades and backups, a managed agent platform is the better fit. Upgrades, database care and the security of the admin login all stay with whoever runs the server.
Frequently asked questions
Does Paperclip need a GPU?
No. Paperclip is a Node.js web app with a PostgreSQL database, and in our lab it ran on 2 CPU cores with about 1 GB of RAM. Agents normally use cloud models. Large local models are too slow without a graphics card, but small local models of 0.5B to 1.5B parameters ran at 13 to 27 tokens per second on CPU in our test.
How much RAM does Paperclip need on a VPS?
In our lab the Paperclip server used 0.89 to 1.01 GB and PostgreSQL 100 to 150 MB at idle, on version v2026.1012.0. A 4 GB server is enough for the control plane; plan 8 GB or more if Claude Code agents or a local model run on the same machine.
Is Paperclip free?
Yes, Paperclip is open source under the MIT licence and you can self-host it at no licence cost. You pay for the server and for the AI models your agents use, for example a Claude or ChatGPT subscription or per-token API billing. Paperclip's budgets pause an agent when it reaches 100 percent of its monthly limit.
Can Paperclip agents use a local model instead of a paid API?
Yes, through the generic "process" adapter, which runs any command you choose. We ran a Python script that sends each task to qwen2.5 0.5B in Ollama and posts the answer back, and a task went from assignment to "done" in 5 to 8 seconds on CPU.
Do I need to install PostgreSQL separately for Paperclip?
No. Without a DATABASE_URL, Paperclip starts an embedded PostgreSQL by itself, and the quickstart compose file uses that. The main docker-compose.yml uses a separate postgres:17-alpine container instead; remove its public 5432 port mapping before starting it on a server.
How do I back up a self-hosted Paperclip?
Dump the database with pg_dump from the Postgres container, copy the paperclip-data Docker volume, and store both on another machine. The secrets master key in that volume must travel with the database dump, or encrypted secrets cannot be restored.
Give your agent company a home
Paperclip belongs on a server that never sleeps, so tasks keep moving while your laptop is closed. Start with a VPS Mini for the control plane, or a VDS Small if the agents run on the same box. Compare all six plans on the VPS and VDS plans page. If you would rather have us install Paperclip for you, ask on Telegram and we will quote the setup work.