|
| 1 | +--- |
| 2 | +title: "Local state" |
| 3 | +linkTitle: "Local state" |
| 4 | +weight: 4 |
| 5 | +description: > |
| 6 | + What the `.score-compose/` and `.score-k8s/` directories contain, when they matter, and when the platform should own state instead. |
| 7 | +--- |
| 8 | + |
| 9 | +Both `score-compose` and `score-k8s` keep state on disk between commands. `score-compose` writes to a `.score-compose/` directory, and `score-k8s` writes to a `.score-k8s/` directory. The `init` command creates the directory; the `generate` command reads from and writes to it on every invocation. |
| 10 | + |
| 11 | +## What's in it |
| 12 | + |
| 13 | +Two kinds of files live in the directory. |
| 14 | + |
| 15 | +`state.yaml` records the workloads added by previous `generate` runs and the resource outputs the implementation produced for them. Random passwords, generated keys, port assignments, and other non-hermetic values are stored here so the next call to `generate` can reuse them instead of producing new values. |
| 16 | + |
| 17 | +`*.provisioners.yaml` files hold the provisioner definitions used to resolve `resources.*` entries in the Score file. `init` creates the default file (`zz-default.provisioners.yaml`); any additional files loaded with `--provisioners` are stored alongside it. |
| 18 | + |
| 19 | +A directory immediately after `score-compose init` looks like this: |
| 20 | + |
| 21 | +```text |
| 22 | +.score-compose/ |
| 23 | +├── state.yaml |
| 24 | +└── zz-default.provisioners.yaml |
| 25 | +``` |
| 26 | + |
| 27 | +`score-k8s init` produces an equivalent layout under `.score-k8s/`. |
| 28 | + |
| 29 | +## Is it mandatory? |
| 30 | + |
| 31 | +Yes, when you use `score-compose` or `score-k8s`. Both `generate` commands expect the directory to exist and fail if you call them without running `init` first. The directory is created and managed by the CLI; you don't edit it by hand. |
| 32 | + |
| 33 | +The CLIs hold no state outside this directory. The `score.yaml` file describes the workload, and everything that needs to persist between `generate` calls is written into the local state. |
| 34 | + |
| 35 | +## When local state helps |
| 36 | + |
| 37 | +The local state is most useful during local development, where the same project is generated and deployed repeatedly. |
| 38 | + |
| 39 | +Take a `postgres` resource as an example: |
| 40 | + |
| 41 | +```yaml |
| 42 | +resources: |
| 43 | + db: |
| 44 | + type: postgres |
| 45 | +``` |
| 46 | +
|
| 47 | +On the first `score-compose generate`, the default `postgres` provisioner produces a random password and writes it to `.score-compose/state.yaml`. On every subsequent `generate`, the provisioner reads the same password back from the state and reuses it. The running database and the application talking to it stay in sync across successive `generate` runs. |
| 48 | + |
| 49 | +Other values that the local state keeps stable across runs include: |
| 50 | + |
| 51 | +- Generated secrets and signing keys returned by resource provisioners. |
| 52 | +- Allocated ports, hostnames, and other non-hermetic resource outputs. |
| 53 | +- The set of workloads accumulated across multiple `generate` calls in the same project. |
| 54 | + |
| 55 | +Without the local state, every `generate` would re-randomize these values and the deployed services would drift apart between runs. |
| 56 | + |
| 57 | +## When you don't need it |
| 58 | + |
| 59 | +The local state is a local-development convenience. It is not the system of record for a deployed application. |
| 60 | + |
| 61 | +In production, prefer to let the target platform own this state: |
| 62 | + |
| 63 | +- On Kubernetes, store secrets in the cluster and let the platform create and rotate them. A resource provisioner that, for example, creates a `DatabaseInstance` should produce its secrets inside the cluster rather than writing them to a developer's working directory. |
| 64 | +- Managed Score implementations and services store resource state on behalf of the deployment, so a workflow does not need to ship `state.yaml` between runs. See [Moving to a managed Score implementation](/docs/how-to/score-cicd-pipelines/#moving-to-a-managed-score-implementation). |
| 65 | + |
| 66 | +Keep the local state for the inner development loop, and let the target platform own state for deployed environments. |
| 67 | + |
| 68 | +## Excluding local state from version control |
| 69 | + |
| 70 | +The local state contains private and potentially sensitive values, including secrets produced by resource provisioners. It must not be committed to source control. |
| 71 | + |
| 72 | +Add the directory to `.gitignore`: |
| 73 | + |
| 74 | +```gitignore |
| 75 | +.score-compose/ |
| 76 | +.score-k8s/ |
| 77 | +``` |
| 78 | + |
| 79 | +## Persisting state in CI/CD |
| 80 | + |
| 81 | +A CI workflow typically starts from a clean slate, with no on-disk state between runs. If you generate from CI against a target where state cannot be delegated to the platform, `state.yaml` has to be carried between runs to keep generated values stable. |
| 82 | + |
| 83 | +The common pattern is to download the state at the start of the job and upload it again at the end. See [Maintaining `score-k8s` state](/docs/how-to/score-cicd-pipelines/#maintaining-score-k8s-state) for a full example that stores the state in a Kubernetes secret. |
| 84 | + |
| 85 | +## Removing local state |
| 86 | + |
| 87 | +To start over, delete the directory and run `init` again: |
| 88 | + |
| 89 | +```bash |
| 90 | +rm -rf .score-compose |
| 91 | +score-compose init |
| 92 | +``` |
| 93 | + |
| 94 | +The next `generate` rebuilds the state from the current Score file. Random outputs will be regenerated with new values, and any drift accumulated across earlier runs is discarded. |
0 commit comments