Skip to content
Browse the docs

Scaling a self-hosted workspace

Updated

On this page

OneCamp is one machine to begin with, and for most teams it stays one. This page is about what that machine needs, what grows first, how to add capacity without touching the machine, and how to move when it is time.

What one machine runs

Every install is the same Docker Compose stack: Postgres, Dgraph, Redis, MinIO for files, EMQX for real-time, OpenSearch for search, LiveKit and its egress service for calls, the collaboration service for shared editing, Traefik in front, and the OneCamp API. ClamAV, which scans uploads, runs when the machine has 7 GB of RAM or more (make tune decides). Four more are optional profiles you turn on when you want them: the Python sandbox for data analysis (code-execution), transcription (transcription), a local model server (local-ai), and the coding agent's runner (coding).

Everything persistent lives under ./data/ next to your compose file. That one fact is what makes the rest of this page short.

Sizing the machine

The floor is 4 GB of RAM and 40 GB of disk, with the swap file the installer creates. At that size the stack uses about 3.2 GB: OpenSearch gets a quarter of the RAM, and ClamAV, which alone holds about 2 GB of virus signatures in memory, is left off, so uploads are accepted without a scan. With 8 GB, make tune turns scanning on, and that is the size to choose for a team that depends on it. Run make create-swap if the installer could not; it is idempotent and it is the difference between a small server that works and one that falls over during a reindex.

OneCamp Cloud runs on 8 GB machines, the size it checks a server against before it will use one.

What grows first depends on how the workspace is used:

  • Files grow the disk. MinIO holds every upload. make storage-report says how full you are and whether a mirrored disk has quietly lost its pair. To get space back, archive what you no longer need and set Remove for good on the Attachments and Recordings policies (Admin, then Archive): anything archived that many days is deleted from storage. Archiving alone hides things and keeps every byte; only Remove for good frees the disk, and it cannot be undone. When the disk itself is the limit, move the files to any S3-compatible bucket without stopping the workspace: STORAGE_ENDPOINT=... STORAGE_BUCKET=... STORAGE_ACCESS=... STORAGE_SECRET=... make storage-attach. It copies, switches, then copies again for what arrived meanwhile, and never deletes from the machine. make storage-detach brings them back, and refuses with the numbers if they would not fit.
  • Memory and calls are sized from the machine. make tune, run by install and by every update, gives search a quarter of the RAM (512 MB to 4 GB), turns virus scanning on at 7 GB or more and off below it, and records calls at 720p on four cores or fewer, so a small machine is not run with a large machine's numbers. Set TUNE_MANUAL=1 in .env to keep your own.
  • Search grows memory. OpenSearch's heap is the first thing to raise on a workspace with a long history.
  • Calls cost CPU while they run. LiveKit is quiet until a meeting starts.
  • Local AI costs whatever the model costs. A model served by Ollama lives in RAM while it answers; a 7B model wants around 8 GB of its own. If you run local models, budget the machine for the model first and the workspace second, or point the workspace at a hosted provider and keep the machine small.

docker stats shows you which of these it is on your machine.

Adding workers without touching the machine

The API process also runs every background loop: the AI teammates' task queue, the scheduler, the workflow engine, the sync and email workers, the sweeps. When the AI teammates need more capacity than one process gives them, or a long agent run is what makes the app feel slow, split the two.

Set SERVICE_ROLE=api in .env so the API process serves requests only, and start workers from the same image:

docker compose --profile workers up -d --scale go-worker=3

Every worker claims from the same queue with a database lease, so adding one never runs a job twice. Each process runs up to AI_AGENT_TASK_CONCURRENCY jobs at once (default 6), so three workers is three times that. Workers serve nothing but a health endpoint and are not on the public network.

Settings an admin saves reach every worker within thirty seconds; nothing needs a restart for that. Admin, then Settings, then System check has an agent-queue line that goes red if due work sits unclaimed for two minutes, which is what a fleet with the API set to api and no worker running looks like.

You can also leave SERVICE_ROLE alone and just add workers; the API process keeps running the loops and the workers add capacity on top.

Scaling the AI itself

The model is usually the bottleneck, not the machine. Three levers, in the order to try them:

  1. Caps. Admin, then AI. A daily token cap per workspace and per teammate, and a per-minute rate limit, keep one busy agent from starving the rest.
  2. Hosted models. A hosted provider scales without you. The machine then only runs the workspace.
  3. Remote agents. A teammate can reason at an endpoint you run elsewhere, over AG-UI, and still work under this workspace's permissions and audit. That moves the heaviest compute off the box entirely.

Moving to a bigger machine

When one machine is genuinely too small, move. The backup is a dump of Postgres and Dgraph, and with BACKUP_FILES=1 a copy of the files too.

On the old machine:

make backup BACKUP_FILES=1
make backup-list

Copy the newest directory under ./data/backups/, your .env, your compose.yml and your LiveKit configuration to the new machine. Install there as you did the first time, then:

make restore FROM=<that directory> CONFIRM=restore

Restore stops the stack, replaces what is there with the backup, migrates, and runs make verify. Point DNS at the new machine last, after make verify is green there. Expect the workspace to be unavailable for the length of the copy.

What to watch

  • make verify after any change. It runs every check the install knows.
  • make enable-backups once. A backup nobody schedules is a backup nobody has. The nightly backup lives on the same disk as the data, so it protects you from a deletion, not from losing the machine; copy it somewhere else too.
  • Admin, then Settings, then System check, for the things that break quietly: a database that stopped answering, search behind, a queue nobody is draining.
  • make doctor before you change .env. It catches a placeholder left in place before the services fail in five different ways.

What does not scale this way

One workspace is one Postgres and one Dgraph. There is no multi-node database and no sharding of a workspace across machines. Workers scale the application; the data tier scales by moving to a bigger machine. For almost every team that is the right trade, and the ones it is not right for know who they are.

Something missing or wrong in this guide? Write to support@onemana.dev.