---
title: Projects
---
# Projects
A **project** is a durable namespace that groups a set of services. Its identity
is its id — the key under the `projects:` map in your manifest. Because a single
resident supervisor can host many projects at once — and a single config file
can declare many projects — projects are how systemg keeps unrelated (or
related) workloads cleanly separated inside one running daemon.
<Info>
One supervisor, many projects. You can run a database stack, a web API, and a
batch of cron units as three independent projects under the **same** systemg
process. They share the runtime but keep separate state, logs, and status —
and you target each one by id with `-p/--project`.
</Info>
## Declaring a project
Projects live under the `projects:` map, keyed by id. One file can declare many:
```yaml
version: "2"
projects:
arbitration:
name: Arbitration
services:
worker:
command: "python worker.py"
gamecast:
services:
api:
command: "python api.py"
```
Each key is the project id. Start the whole file and all its projects boot; the
first project becomes the primary and the rest register behind it. See
[Configuration](/how-it-works/configuration) for the full schema.
<Warning>
Treat the project id as durable runtime identity. Changing it does not rename a
project — it creates a **new** namespace, and the old one's running services
become orphaned state (visible under `sysg status --all`). Rename freely with
`name`; never rename by editing the id.
</Warning>
<Note>
Top-level `services:` with no project form a **loose bundle** — they still run,
and their state persists under `projects/__loose__/`. The older singular
`project:` block still parses (with a deprecation warning) for existing
single-project manifests; convert them with
[`sysg migrate`](/how-it-works/commands/migrate). Prefer the `projects:` map
once more than one project shares a supervisor.
</Note>
## `-p/--project` vs `-c/--config`
These two flags answer different questions, and knowing which to reach for is
the single most useful thing about projects.
| Flag | What it does |
|------|--------------|
| `-c` / `--config` | **Loads and registers** the config file on disk — **every project it declares**, plus any loose bundle. Use it the first time you start, or when you want to reload the manifest from a specific path. |
| `-p` / `--project` | **Targets a single project already registered** with the running supervisor, by its id. No file path required. |
The mental model: `-c` is *bring this file's projects into the supervisor from
disk*; `-p` is *act on one project the supervisor already knows about*.
<Info>
`-c <file>` operates on the file as a whole. `sysg status -c stack.yaml` shows
every project the file declares, not just the first — the file identifies
"everything here," and `-p` narrows to one.
</Info>
```sh
# First start: register the project from its config
$ sysg start -c services/arbitration.yaml
# From now on, target it by id — no config path needed
$ sysg status -p arbitration
$ sysg logs -p arbitration -s worker
$ sysg restart -p arbitration
$ sysg stop -p arbitration
```
<Info>
Every registered project stores the config path it was started from. That's
why `-p` alone is enough for later commands — systemg looks up the project's
recorded manifest for you. This is what makes multi-project workflows
ergonomic: you register once with `-c`, then drive everything by `-p`.
</Info>
## How `-p` relates to `sysg restart`
`restart -p <id>` is where the stored config path pays off:
```sh
$ sysg restart -p arbitration
```
When `--config` is omitted, `restart --project` reuses the config path the
supervisor already recorded for that project and reloads it from disk. Manifest
changes are applied on reload:
- services added since the last load **start**
- services removed from the manifest **stop**
- changed commands **take effect**
You do not need to pass `-c` again once the project has a known config path.
Pass `-c` explicitly only when you want to reload from a *different* file.
<Warning>
If the config you point at declares a different `project.id` than the one you
target, systemg refuses the operation rather than silently switching
namespaces. The flag, the config, and any service selector prefix must all
agree on the project.
</Warning>
## Qualified service selectors
Anywhere you name a service, you can qualify it with its project using
`project_id/service_name`:
```sh
$ sysg restart -s arbitration/worker
$ sysg logs -s gamecast-dev/api
```
This is equivalent to passing `-p` plus the bare service name. If you supply
both a selector prefix and `-p`, they must match.
<Warning>
A mismatch is an error, not a best-effort guess:
```
project flag 'arbitration' does not match service selector project 'gamecast-dev'
```
systemg would rather stop than act on the wrong project.
</Warning>
<Info>
When a service name is unambiguous — it exists in exactly one registered
project — you can drop the project qualifier entirely and systemg resolves it
for you. If the same service name lives in **multiple** projects, systemg
refuses and asks you to disambiguate with `-p`.
</Info>
## `-p` per command
`-p/--project` is accepted across the commands that act on running projects:
| Command | Effect of `-p <id>` |
|---------|---------------------|
| `start` | Start (or start a single service within) the named project. |
| `stop` | Stop the named project's services. Use `--supervisor` to shut everything down. |
| `restart` | Reload the project's stored manifest and restart its services. |
| `status` | Scope the status table to one project. |
| `logs` | Filter tailed logs to one project. |
| `inspect` | Inspect a service within a specific project. |