The core orchestration engine for Cosy (Cost Optimised Server Yard).
This Spring Boot application acts as the "Town Hall" of the Cosy ecosystem. It manages game server containers, handles
file I/O operations directly on the host, and coordinates with specialized data stores for metrics, logs, and backups.
- Language: Java 21
- Framework: Spring Boot 3.x
- Build Tool: Maven
- Database: PostgreSQL (Data), InfluxDB (Metrics), Grafana Loki (Logs)
- Runtime Support: Docker (Native)
- Java 21 SDK (Required)
- Docker Desktop / Engine (Required for the runtime & dev infrastructure)
- Maven (Optional, using the wrapper is recommended)
Before running the backend, you need the "Data Silos" (Postgres, Influx, etc.) running. We provide a docker-compose file for development.
cd infrastructure
docker compose up -dEnsure your application.properties or application.yaml is configured to connect to the services started in step 1.
You can start the application using the Maven wrapper:
./mvnw spring-boot:runThe API will be available at http://localhost:8080/api.
Cosy supports custom, game-specific metrics published directly by your game server (for example via a Minecraft mod/plugin). This is useful for values that Cosy cannot collect automatically, such as:
playerCounttpsmspt- current game state / map name
- modpack-specific stats
The server publishes a JSON object (a simple key/value map). Cosy stores this as the serverโs current custom metrics and will use it for display/streaming.
Your game server process/container must have these environment variables set (your mod/plugin reads them at runtime):
-
COSY_BACKEND_URL
Base URL of the Cosy backend (e.g.https://<your-domain>) -
COSY_GAMESERVER_UUID
The UUID of this game server in Cosy -
COSY_CONTAINER_SECRET
Secret used to authenticate custom metric updates
Tip: Read these once at server startup and fail fast (log a clear error) if any are missing.
Before publishing metrics, verify that your credentials are correct by calling the validation endpoint:
- Method:
GET - Goal: Ensure the backend is reachable and that the
(uuid, secret)pair is accepted.
If validation fails (non-2xx), do not spam updates. Log the error and retry with backoff.
Note: The exact endpoint path depends on your backend API routes. Use the validation endpoint exposed by the Cosy backend (see the Metrics/GameServer controllers in this repo).
To publish or update custom metrics, send the current metrics map to Cosy:
Use the following endpoints:
PUT /api/internal/game-server/custom-metric/{game-server-uuid}
Body: JSON object (flat key/value map)
{
"playerCount": 12,
"tps": 19.8,
"mspt": 5.3,
"motd": "Vanilla+ SMP",
"pvpEnabled": true
}Headers:
Authorization: `{secret}`
Response: 2xx
GET api/internal/game-server/test-connection/{game-server-uuid}
Headers:
Authorization: `{secret}`
Response 2xx with body:
{
"data": false, // <-- true if connection successful
"error": null,
"path": "/api/internal/game-server/test-connection/f98585aa-78d0-4fdf-9b3b-2f4b8c66d6e0",
"status_code": 200,
"success": true,
"timestamp": "2026-02-18T20:29:54.046171033Z"
}Cosy treats this payload as the serverโs current custom metric holder. Publish on an interval (e.g. every 5โ10 seconds) and/or when values change.
onServerStart:
url = env("COSY_BASE_URL")
uuid = env("COSY_GAMESERVER_UUID")
secret = env("COSY_CONTAINER_SECRET")
GET url + <validate-endpoint> using uuid + secret
if not ok:
throw error
every 5s:
metrics = { "playerCount": getOnlinePlayers(), "tps": getTps(), "mspt": getMspt() }
PUT url + <custom-metrics-endpoint> using uuid + secret body = metrics
- Keep metric keys stable (e.g. always
playerCount, not sometimesplayers). - Use primitive values: number, string, boolean.
Cosy uses a Postgres instance for data storage and Loki for Server Logs.
The Postgres setup is straightforward and can be found in the infrastructure folder.
The Loki setup is a bit more involved as Loki itself doesn't have any authorization mechanism, so we use an nginx
reverse proxy in front of it to add basic auth. For this inside this Repository we have a infrastructure/htpasswd file checked in, which contains a development username/password: [user: loki-user, password: loki-password].
This needs to be changed in prod. For this you can use this command to generate a password hashed htpasswd file:
powershell:
docker run --rm httpd:2.4-alpine htpasswd -nbB loki-user loki-password | Out-File -Encoding ASCII htpasswdbash:
docker run --rm httpd:2.4-alpine htpasswd -nbB loki-user loki-password > infrastructure/htpasswdWe maintain a strict code style using Spotless and Google Java Format (AOSP style).
To verify if your code meets the standards without modifying it:
./mvnw spotless:checkTo automatically format your code:
./mvnw spotless:applyPro Tip: It is highly recommended to run spotless:apply before pushing any code to avoid CI failures.
The backend uses a Strategy Pattern to handle different environments without changing application logic.
- RuntimeService Interface: The main contract for server management.
- DockerRuntimeStrategy: Uses the local Docker Socket (/var/run/docker.sock). Used for single-node setups.
Unlike traditional cloud apps, Cosy uses Direct I/O for file management. Cosy will attempt to utilize a native linux library to ensure secure file operations. When running on other operating systems, this security cannot be ensured so a potentially vulnerable fallback will be used.
- Docker Mode: Uses Java NIO to read Bind Mounts directly on the host.
Run unit and integration tests:
./mvnw test
Once the application is running, you can explore the REST API via Swagger UI:
To access the PostgreSQL database running in Docker:
docker exec -it cosy-postgres psql -U cosy -d cosyCommon Commands:
\d- List all tables, views, and sequencesSELECT * FROM <table_name>;- Query data\q- Exit psql
To avoid DB corruption or reset to a clean state (re-initialize dummy data), remove the volumes and restart the infrastructure:
cd infrastructure
docker compose down -v
docker compose up -d