File-level host-path compose volumes (e.g. `../config/foo.sh:/opt/foo.sh`)
were synthesized into a kind extraMount + hostPath PV chain with a
sanitized containerPath (`/mnt/host-path-<sanitized>`). The sanitized
name is derived from the compose volume source and is identical across
deployments of the same stack, so two deployments sharing a cluster
collided at the containerPath — kind only honors the first deployment's
bind, subsequent deployments' pods silently read the first's content.
The same code path was also broken on real k8s, which has no way to
populate `/mnt/host-path-*` on worker nodes.
File-level compose binds are conceptually k8s ConfigMaps. The snowball
stack already uses the ConfigMap-backed named-volume pattern by hand.
Make that automatic at the k8s object-generation layer, without
touching deployment-dir compose or spec files.
Behavior at deploy create (validation only, no file mutation):
- :rw on a host-path bind -> DeployerException (use a named
volume for writable data)
- Directory with subdirectories -> DeployerException (embed in image,
split into configmaps, or use
initContainer)
- Directory or file > ~700 KiB -> DeployerException (ConfigMap budget)
- File, or flat small directory -> accepted, handled at deploy start
Behavior at deploy start:
- cluster_info.get_configmaps() additionally walks pod + job compose
volumes and emits a V1ConfigMap per host-path bind (deduped by
sanitized name across all pods/services). Content read from
{deployment_dir}/config/<pod>/<file> (already populated by
_copy_extra_config_dirs).
- volumes_for_pod_files emits V1ConfigMapVolumeSource instead of
V1HostPathVolumeSource for host-path binds.
- volume_mounts_for_service stats the source and sets V1VolumeMount
sub_path to the filename when source is a regular file — single-key
ConfigMaps land as files, whole-dir ConfigMaps land as directories.
- _generate_kind_mounts no longer emits `/mnt/host-path-*` extraMounts
for these binds (the ConfigMap path bypasses kind node FS entirely).
Deployment dir layout is unchanged. Compose files, spec.yml, and
{deployment_dir}/config/<pod>/ remain exactly as today — trivially
diffable against stack source, no synthetic volume names. ConfigMaps
are visible only in k8s (kubectl get cm -n <ns>).
The existing `/mnt/host-path-*` skip in check_mounts_compatible is
retained as a transition tolerance for deployments created before
this change.
Updates:
- deployment_create: _validate_host_path_mounts() called per pod/job
in the create loops; 700 KiB ConfigMap budget (accounts for base64
+ metadata overhead)
- helpers: _generate_kind_mounts skips host-path entries;
volumes_for_pod_files emits ConfigMap-backed V1Volume;
volume_mounts_for_service takes optional deployment_dir and
auto-sets sub_path for single-file sources
- cluster_info: new _host_path_bind_configmaps() walked from
get_configmaps(); volume_mounts_for_service call passes
deployment_dir from spec.file_path
- docs: document the behavior and the rejected shapes in
deployment_patterns.md
- tests: k8s-deploy asserts the host-path ConfigMaps exist,
compose/spec unchanged, and no `/mnt/host-path-*` extraMounts
Refs: so-b86
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .github/workflows | ||
| .pebbles | ||
| docs | ||
| scripts | ||
| stack_orchestrator | ||
| tests | ||
| .gitignore | ||
| .pre-commit-config.yaml | ||
| AI-FRIENDLY-PLAN.md | ||
| CLAUDE.md | ||
| LICENSE | ||
| MANIFEST.in | ||
| README.md | ||
| STACK-CREATION-GUIDE.md | ||
| TODO.md | ||
| laconic-network-deployment.md | ||
| pyproject.toml | ||
| pyrightconfig.json | ||
| requirements.txt | ||
| setup.py | ||
| tox.ini | ||
| uv.lock | ||
README.md
Stack Orchestrator
Stack Orchestrator allows building and deployment of a Laconic Stack on a single machine with minimial prerequisites. It is a Python3 CLI tool that runs on any OS with Python3 and Docker. The following diagram summarizes the relevant repositories in the Laconic Stack - and the relationship to Stack Orchestrator.
Install
To get started quickly on a fresh Ubuntu instance (e.g, Digital Ocean); try this script. WARNING: always review scripts prior to running them so that you know what is happening on your machine.
For any other installation, follow along below and adapt these instructions based on the specifics of your system.
Ensure that the following are already installed:
- Python3:
python3 --version>=3.8.10(the Python3 shipped in Ubuntu 20+ is good to go) - Docker:
docker --version>=20.10.21 - jq:
jq --version>=1.5 - git:
git --version>=2.10.3
Note: if installing docker-compose via package manager on Linux (as opposed to Docker Desktop), you must install the plugin, e.g. :
mkdir -p ~/.docker/cli-plugins
curl -SL https://github.com/docker/compose/releases/download/v2.11.2/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose
chmod +x ~/.docker/cli-plugins/docker-compose
Next decide on a directory where you would like to put the stack-orchestrator program. Typically this would be
a "user" binary directory such as ~/bin or perhaps /usr/local/laconic or possibly just the current working directory.
Now, having selected that directory, download the latest release from this page into it (we're using ~/bin below for concreteness but edit to suit if you selected a different directory). Also be sure that the destination directory exists and is writable:
curl -L -o ~/bin/laconic-so https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so
Give it execute permissions:
chmod +x ~/bin/laconic-so
Ensure laconic-so is on the PATH
Verify operation (your version will probably be different, just check here that you see some version output and not an error):
laconic-so version
Version: 1.1.0-7a607c2-202304260513
Save the distribution url to ~/.laconic-so/config.yml:
mkdir ~/.laconic-so
echo "distribution-url: https://github.com/cerc-io/stack-orchestrator/releases/latest/download/laconic-so" > ~/.laconic-so/config.yml
Update
If Stack Orchestrator was installed using the process described above, it is able to subsequently self-update to the current latest version by running:
laconic-so update
Usage
The various stacks each contain instructions for running different stacks based on your use case. For example:
Deployment Types
- compose: Docker Compose on local machine
- k8s: External Kubernetes cluster (requires kubeconfig)
- k8s-kind: Local Kubernetes via Kind - one cluster per host, shared by all deployments
External Stacks
Stacks can live in external git repositories. Required structure:
<repo>/
stack_orchestrator/data/
stacks/<stack-name>/stack.yml
compose/docker-compose-<pod-name>.yml
deployment/spec.yml
Deployment Commands
# Create deployment from spec
laconic-so --stack <path> deploy create --spec-file <spec.yml> --deployment-dir <dir>
# Start (creates cluster on first run)
laconic-so deployment --dir <dir> start
# GitOps restart (git pull + redeploy, preserves data)
laconic-so deployment --dir <dir> restart
# Stop
laconic-so deployment --dir <dir> stop
spec.yml Reference
stack: stack-name-or-path
deploy-to: k8s-kind
network:
http-proxy:
- host-name: app.example.com
routes:
- path: /
proxy-to: service-name:port
acme-email: admin@example.com
config:
ENV_VAR: value
SECRET_VAR: $generate:hex:32$ # Auto-generated, stored in K8s Secret
volumes:
volume-name:
Contributing
See the CONTRIBUTING.md for developer mode install.
Platform Support
Native aarm64 is not currently supported. x64 emulation on ARM64 macos should work (not yet tested).
