Ozwald is a multi-tool for container provisioning.
Project description
Ozwald
Simple, pragmatic infrastructure for provisioning containerized AI services.
Current release: v0.x — “Ozwald is a provisioner of containerized services.”
Ozwald grew out of real-world friction provisioning LLM containers across mixed hardware (different GPUs, or CPU-only) with varying runtime parameters. It focuses on the glue: a small, well-typed config, a provisioner API, and a CLI to make starting and stopping services predictable.
While this release concentrates on provisioning, Ozwald is designed to evolve into a broader orchestration framework for AI systems, covering pipelines, scheduling, and environments beyond a single host.
Key ideas
- A clear model for describing services using “varieties” (e.g., nvidia, amdgpu, cpu-only) and “profiles” (named parameter sets like fast-gpu, no-gpu, etc.).
- A provisioner API exposing configured and active services, host resources, and a small profiling queue.
- A CLI for standing up the provisioner and inspecting state locally.
- Works best as a library dependency that your orchestrator or application depends on.
Status
- Early release. APIs and configuration formats may change.
- Some features are scaffolding that will expand in subsequent releases (orchestration, multi-host scheduling, extensibility hooks).
Installation
- Add to your project’s dependencies (recommended):
pip install ozwald
Or include in your pyproject.toml/requirements.txt as you would any other library. Ozwald is typically used by another project (for example, an orchestrator service) rather than invoked directly by end users.
Quick start
- Provide a settings file
Create a YAML configuration that declares hosts, provisioners, and services. For example:
---
hosts:
- name: localhost
ip: 127.0.0.1
provisioners:
- name: local
host: localhost
cache:
type: redis
parameters:
host: ozwald-provisioner-redis
port: 6379
db: 0
services:
- name: qwen1.5-vllm
type: container
description: DeepSeek Qwen 1.5B via vLLM
varieties:
nvidia:
image: openai-api-vllm.nvidia
environment:
GPU: true
amdgpu:
image: openai-api-vllm.amdgpu
environment:
GPU: true
cpu-only:
image: openai-api-vllm.cpu-only
environment:
GPU: false
environment:
MODEL_NAME: deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B
profiles:
no-gpu:
environment:
MAX_MODEL_LEN: 45000
fast-gpu:
environment:
GPU_MEMORY_UTILIZATION: 0.9
CPU_OFFLOAD_GB: ""
- name: fiction-sources
type: source-files
description: Fiction sources in EPUB format
environment:
DIRECTORY: sources/
What this expresses:
- hosts: Named machines and their IPs (local or remote).
- provisioners: A provisioner runs on a host and uses a cache (Redis) for state.
- services: Descriptions of containerized services plus non-container resources (like source-files) with an environment.
- varieties: Hardware-specific container images and environment toggles.
- profiles: Named overlays for environment variables to switch runtime behavior.
- Set a system key
The provisioner API requires a bearer token for access. Set an environment variable before starting your stack:
export OZWALD_SYSTEM_KEY="your-long-random-token"
- Start the provisioner (local dev)
From a shell, use the CLI to spin up the provisioner network and containers required for the API and cache:
ozwald start_provisioner --api-port 8000 --redis-port 6379
Then check status:
ozwald status
- Call the API
With the provisioner running, query configured or active services:
curl -H "Authorization: Bearer $OZWALD_SYSTEM_KEY" \
http://127.0.0.1:8000/srv/services/configured/
curl -H "Authorization: Bearer $OZWALD_SYSTEM_KEY" \
http://127.0.0.1:8000/srv/services/active/
Configuration reference
Top-level keys:
-
hosts[]name: stringip: string
-
provisioners[]name: stringhost: name of a hostcache:type: currentlyredisparameters.host: Redis hostnameparameters.port: Redis portparameters.db: Redis DB index
-
services[]name: unique service nametype:containeror other provider types (e.g.,source-files)description: optionalimage: default image for simple cases (optional when usingvarieties)environment: base environment mapvarieties:- freeform keys such as
nvidia,amdgpu,cpu-only, each containing:image: container imageenvironment: overlay env vars
- freeform keys such as
profiles:- freeform keys such as
fast-gpu,no-gpu, etc., each containing:environment: overlay env vars
- freeform keys such as
Example: combining a variety and profile at runtime tells the orchestrator which image to use and which environment overlays to apply when provisioning.
CLI usage
The ozwald command provides a small set of actions for local development and inspection:
ozwald <action> [--api-port N] [--redis-port N] [--port N] [--no-restart] [--use-api]
Actions:
start_provisioner Start local provisioner network and containers
stop_provisioner Stop provisioner-related containers
list_configured_services List services from the provisioner config via API
list_active_services List services currently active or activating
show_host_resources Show CPU/RAM/GPU/VRAM of the host (optionally via API)
status Summarize provisioner health (network and containers)
Options:
--api-port N Port for provisioner API (default: 8000)
--redis-port N Port for provisioner Redis (default: 6379)
--port N API port used for list/show actions (default: --api-port)
--no-restart Do not restart containers if already running
--use-api For show_host_resources, fetch via provisioner API
Examples:
ozwald start_provisioner --api-port 8000 --redis-port 6379
ozwald status
ozwald list_configured_services --port 8000
ozwald list_active_services --port 8000
ozwald show_host_resources --use-api --port 8000
ozwald stop_provisioner
Provisioner API
Base URL: http://<host>:<port> (default 127.0.0.1:8000)
Authentication: All non-health endpoints require a bearer token via header:
Authorization: Bearer <OZWALD_SYSTEM_KEY>
Endpoints:
-
GET /health- No authentication required. Returns
{ "status": "healthy" }.
- No authentication required. Returns
-
GET /srv/services/configured/- Returns the list of configured service definitions.
-
GET /srv/services/active/- Returns the list of services currently active (or transitioning).
-
POST /srv/services/active/update/- Body: JSON array of
ServiceInformationobjects expressing the desired active set. - Response:
{ "status": "accepted", "message": "..." }or an error status.
- Body: JSON array of
-
GET /srv/resources/available/- Returns a snapshot of currently available resources on the host.
-
GET /srv/host/resources- Returns a structured summary of CPU, RAM, GPU, and VRAM on the host.
-
GET /srv/services/profile- Returns pending profiling requests.
-
POST /srv/services/profile- Queues a profiling request while the system is unloaded.
Example requests:
curl -s -H "Authorization: Bearer $OZWALD_SYSTEM_KEY" \
http://127.0.0.1:8000/srv/host/resources | jq
curl -s -H "Authorization: Bearer $OZWALD_SYSTEM_KEY" \
http://127.0.0.1:8000/srv/services/active/ | jq
curl -s -X POST -H "Authorization: Bearer $OZWALD_SYSTEM_KEY" \
-H 'Content-Type: application/json' \
-d '[{"name":"qwen1.5-vllm","variety":"cpu-only","profile":"no-gpu"}]' \
http://127.0.0.1:8000/srv/services/active/update/
Python usage (API client)
In most projects you will call the HTTP API from your orchestrator. A minimal example using requests:
import os
import requests
base = os.getenv("OZWALD_BASE", "http://127.0.0.1:8000")
token = os.environ["OZWALD_SYSTEM_KEY"]
headers = {"Authorization": f"Bearer {token}"}
# List configured services
cfg = requests.get(f"{base}/srv/services/configured/", headers=headers).json()
# Activate a service
desired = [{"name": "qwen1.5-vllm", "variety": "cpu-only", "profile": "no-gpu"}]
r = requests.post(f"{base}/srv/services/active/update/", json=desired, headers=headers)
r.raise_for_status()
Extensibility and roadmap
The provisioner is the first building block. The roadmap includes:
- A richer orchestration layer (multi-host, scheduling, and lifecycle policies).
- First-class pipeline support (ingest, chunk, embed, index, serve) as composable services.
- Pluggable backends for cache/state and metrics.
- Declarative operations: dry-run planning, diffs, and explainers for changes to active services.
Development
The repo includes a small developer environment and tests. Common tasks are under tasks/ and there are integration and unit tests under tests/.
If you want to run the API directly for local development:
export OZWALD_SYSTEM_KEY=dev-secret
uvicorn api.provisioner:app --host 127.0.0.1 --port 8000
License & Contributing
Ozwald is open source software, designed to encourage broad adoption while ensuring the core technology remains free.
- The Core: AGPLv3
The Ozwald engine, orchestrator, and provisioner code are licensed under the GNU Affero General Public License v3 (AGPLv3).
Copyright: © Fred McDavid.
What this means: You are free to use Ozwald for your own projects or within your company. You can modify it for internal use.
The Restriction: If you modify the Ozwald core and make it available to users over a network (e.g., as a hosted service or SaaS), you must release your modifications to the community under the same AGPLv3 license.
Why: This ensures that improvements to the Ozwald core flow back to the community and prevents proprietary forks of the platform infrastructure.
- Your Apps: Safe to Build
I want you to build proprietary, commercial applications using Ozwald without fear.
The Boundary: The Ozwald Client SDKs and public interfaces are licensed under the Apache 2.0 License (permissive).
The Result: Linking to Ozwald or using the interfaces to build your AI assistant does not force you to open source your application logic. Your data and your business logic remain yours.
- Contributing & Copyright
Contributions are welcome! To ensure the project can evolve and remains sustainable, I require all contributors to sign a Contributor License Agreement (CLA).
Why a CLA? This ensures that I (Fred McDavid) retain the copyright to the codebase. This centralization of ownership allows me to defend the project legally and preserves the option to offer commercial licensing in the future.
The Process: A bot will prompt you to sign the CLA via a simple click when you open your first Pull Request.
- Commercial Licensing
If you require a license that allows for proprietary modification of the core platform, or have specific compliance needs that the AGPL cannot meet, please contact me directly at fred@frameworklabs.us to discuss commercial licensing options.
Attribution
Author: Fred McDavid
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ozwald-0.0.1.tar.gz.
File metadata
- Download URL: ozwald-0.0.1.tar.gz
- Upload date:
- Size: 46.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be5b28398301c9654ef395dce6b4ffff2874dabaed307e440ebfac3d3cb35787
|
|
| MD5 |
ac81b4c1168bd54fa7349caeca67c70c
|
|
| BLAKE2b-256 |
8b5a91d74c80b9cd5be92787cd50a36b26ac3b496cf45cc33c9867cb45718a3b
|
File details
Details for the file ozwald-0.0.1-py3-none-any.whl.
File metadata
- Download URL: ozwald-0.0.1-py3-none-any.whl
- Upload date:
- Size: 47.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Arch Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af7d2a66be84b4552a2777824a523f84f5bd02507aa41c276a2ee68e2c4ca216
|
|
| MD5 |
24681b2ddccfbb33be26c4e513d9995c
|
|
| BLAKE2b-256 |
74de2c11787e702b6d9b6d15d315acfdbe122a95ab92b386bb8e62bf0e0b3f41
|