Skip to content

Architecture

One principle drives Shardkeep: the Controller owns desired state; Nodes own execution.

The Controller decides what should exist and how it should run. It holds:

  • the web UI and the REST API;
  • PostgreSQL with users, teams, roles, servers, schedules, backups and the audit log;
  • a certificate authority that issues each Node’s certificate;
  • a cache of verified server software and plugins.

It never runs Docker commands and never connects to a Node.

Each Node runs an Agent with access to its Docker Engine. The Agent:

  • connects out to the Controller and keeps the session alive with heartbeats;
  • receives each of its servers’ specs (software, resources, ports, plugins, schedules), keeps a copy on disk, and makes Docker match them;
  • reports what is actually happening: each server’s state, the Node’s capacity and usage, and conditions when something needs attention;
  • runs schedules and backups by itself.

Shardkeep keeps three kinds of state apart:

  • Desired: what you asked for. Each change gives a server’s spec a new generation number.
  • Observed: what the Agent last reported, including which generation it has applied.
  • History: events and the audit log.

When observed and desired differ for a reason that is not just “in progress”, the server or Node carries a condition with a reason code (see Conditions) rather than an extra state.

A server is a logical workload. Its data lives in a Docker volume named after the server, which outlives containers: changing resources or software replaces the container and keeps the volume, and migration moves the data to a volume on another Node. Nothing in Shardkeep is keyed on a container ID.

Servers run from Shardkeep’s templates (Paper, Purpur, Folia, Vanilla, Velocity) on its own Java runtime images. Arbitrary Docker images are not supported: templates are what let Shardkeep stop, back up and update servers safely.