Scaling PikoCI¶
PikoCI is designed to grow with your needs. You can start with a single binary and zero external dependencies, then scale to distributed workers and production-grade databases without changing your pipeline config.
Phase 1 — In-memory, single machine¶
The fastest way to get started. Everything runs in a single process. No files, no external services, no configuration.
pikoci server \
--db-system mem \
--pubsub-system mem \
--run-worker \
--jwt-secret my-secret \
--pipeline-name my-pipeline \
--pipeline-config pipeline.hcl
What this gives you: - Server + worker in one process - Pipeline loaded at startup, ready immediately - Zero external dependencies
The trade-off: - All data is lost on restart - Only one worker
Good for: local development, trying things out, CI for small personal projects.
Phase 2 — SQLite, single machine¶
Add persistence without any new infrastructure. One flag change.
pikoci server \
--db-system sqlite \
--db-name pikoci.db \
--pubsub-system mem \
--run-worker \
--jwt-secret my-secret \
--pipeline-name my-pipeline \
--pipeline-config pipeline.hcl
What this gives you: - Build history survives restarts - Resource versions and cursors persist - Still zero external dependencies
Note: From Phase 2 onward, pipelines persist in the database. You can
manage them via the web UI or CLI (pikoci client pipelines create ...)
instead of passing --pipeline-name / --pipeline-config on every start.
The flags still work — they create or update the pipeline at startup.
Migrating from in-memory:
If you started with in-memory and want to keep your data, export it first:
# while the server is still running
pikoci client export --url http://localhost:8080 --output pikoci.db
# stop the server, restart with SQLite pointing at the exported file
pikoci server --db-system sqlite --db-name pikoci.db ...
Good for: teams that want history, projects that need to survive restarts.
Phase 3 — Distributed workers with NATS¶
Add more workers without changing the server. Workers can run on any machine that has network access to the server and to NATS.
Why NATS: NATS is the simplest external queue to set up — a single binary, no configuration needed for basic use. If you don't have a queue already, start with NATS.
Step 1 — Start NATS:
Step 2 — Restart the server without a built-in worker:
export NATS_SERVER_URL="nats://localhost:4222"
pikoci server \
--db-system sqlite \
--db-name pikoci.db \
--pubsub-system nats \
--run-worker=false \
--jwt-secret my-secret
Step 3 — Start workers on any machine:
# generate a worker token (or copy it from the server startup logs)
pikoci worker-token --jwt-secret my-secret
# start worker 1 (same machine or different)
export NATS_SERVER_URL="nats://your-nats-host:4222"
pikoci worker \
--pikoci-url http://your-server:8080 \
--pubsub-system nats \
--worker-token <token>
# start worker 2
pikoci worker \
--pikoci-url http://your-server:8080 \
--pubsub-system nats \
--worker-token <token>
Add as many workers as you need. NATS distributes jobs between them automatically. Workers can be on different machines, in different networks, or behind NAT — they only need outbound access to NATS and the server API.
Dedicated workers with --queues:
PikoCI uses two separate queues: one for jobs and one for resource checks. You can run specialized workers to prevent long-running jobs from blocking resource version detection:
# worker that only runs jobs (e.g. on a beefy machine)
pikoci worker --queues jobs --pubsub-system nats --worker-token <token> ...
# worker that only checks resources (lightweight, never blocked by jobs)
pikoci worker --queues checks --pubsub-system nats --worker-token <token> ...
# worker that handles both (default)
pikoci worker --pubsub-system nats --worker-token <token> ...
What this gives you: - Multiple workers running jobs in parallel - Workers can be on any machine - Workers can come and go without affecting the server - Jobs survive worker crashes — NATS requeues unacknowledged messages
Note: Switching from --pubsub-system mem to nats means the queue
changes. Any builds that were pending in the in-memory queue will not carry
over — they'll need to be retriggered.
Good for: teams with multiple projects, jobs that need specific hardware, isolating workloads.
Phase 4 — Production with PostgreSQL¶
Replace SQLite with PostgreSQL for better performance, concurrent access, and the ability to run multiple server instances.
export NATS_SERVER_URL="nats://nats-host:4222"
pikoci server \
--db-system postgresql \
--db-host db.example.com \
--db-port 5432 \
--db-user pikoci \
--db-password secret \
--db-name pikoci \
--pubsub-system nats \
--run-worker=false \
--jwt-secret my-secret
Migrating from SQLite to PostgreSQL:
pgloader handles SQLite to PostgreSQL migration in
one command. Install it first (apt install pgloader, brew install pgloader,
or use the Docker image), then:
PikoCI runs database migrations automatically on startup, so after pgloader
copies the data, just restart the server with --db-system postgresql and
migrations will bring the schema up to date if needed.
Multiple server instances:
With PostgreSQL you can run multiple server instances behind a load balancer.
The DB-backed scheduler uses SELECT ... FOR UPDATE SKIP LOCKED to ensure
each resource check is processed by only one instance.
# instance 1
pikoci server --db-system postgresql --db-host db.example.com --db-name pikoci \
--pubsub-system nats --run-worker=false --jwt-secret my-secret ...
# instance 2 (same config, different machine)
pikoci server --db-system postgresql --db-host db.example.com --db-name pikoci \
--pubsub-system nats --run-worker=false --jwt-secret my-secret ...
What this gives you: - High availability — server instances can restart without downtime - Better performance for large builds and many pipelines - Multiple server instances behind a load balancer
Good for: production deployments, teams that need HA, large-scale CI.
Summary¶
| Phase | Database | Queue | Workers | Use case |
|---|---|---|---|---|
| 1 | Memory | Memory | Built-in | Development, trying things out |
| 2 | SQLite | Memory | Built-in | Small teams, persistence needed |
| 3 | SQLite | NATS | Distributed | Multiple workers, any machine |
| 4 | PostgreSQL | NATS | Distributed | Production, high availability |
The pipeline config never changes between phases. Add infrastructure when you need it — not before.
Other supported backends¶
Databases: SQLite, MySQL, MariaDB, PostgreSQL
Queues: in-memory, NATS, RabbitMQ, Kafka
See Database Backends and Queue Backends for full configuration reference.