OCI Stack Bundles
ReadyStackGo can pull stack definitions directly from OCI container registries like Docker Hub, GitHub Container Registry (GHCR), or Azure Container Registry. This enables versioned stack distribution via CI/CD pipelines and deterministic deployments with lock files.
Overview
Section titled “Overview”| Feature | Description |
|---|---|
| OCI Registry Source | Add a container registry as a stack source |
| Tag-based Versioning | Each registry tag represents a stack version |
| Glob Tag Filtering | Filter tags with patterns like v*, ams-* |
| Lock File Support | Pin images to digests for deterministic deployments |
| Test Connection | Verify registry access before adding a source |
| Authentication | Support for private registries with username/password |
Step by Step: Adding an OCI Registry Source
Section titled “Step by Step: Adding an OCI Registry Source”Step 1: Navigate to Stack Sources
Section titled “Step 1: Navigate to Stack Sources”Go to Settings → Stack Sources → Add Source.
Step 2: Select OCI Registry
Section titled “Step 2: Select OCI Registry”Choose OCI Registry from the source type selection.
Step 3: Configure the Source
Section titled “Step 3: Configure the Source”Fill in the registry details:
- Source ID – Unique identifier (e.g.,
my-oci-stacks) - Display Name – Human-readable name
- Registry Host – Registry hostname without protocol (e.g.,
docker.io,ghcr.io,myregistry.azurecr.io) - Repository – Full repository path (e.g.,
myorg/rsgo-stacks) - Tag Pattern – Glob pattern to filter tags (default:
*= all tags)
Step 4: Configure Authentication (Optional)
Section titled “Step 4: Configure Authentication (Optional)”For private registries, provide credentials:
- Username – Registry username
- Password / Token – Access token or password
| Registry | Username | Password |
|---|---|---|
| Docker Hub | Docker Hub username | Access Token |
| GHCR | GitHub username | PAT with read:packages |
| Azure CR | Service Principal ID | Service Principal Secret |
Step 5: Test Connection
Section titled “Step 5: Test Connection”Click Test Connection to verify access. The test lists available tags and shows a preview of the first 10 tags.
Step 6: Create Source
Section titled “Step 6: Create Source”Click Create Source to add the registry. RSGO will immediately sync and load stacks from matching tags.
OCI Stack Bundle Format
Section titled “OCI Stack Bundle Format”An OCI Stack Bundle is a container image with stack definition files as layers.
Bundle Structure
Section titled “Bundle Structure”| Layer | Media Type | Content | Required |
|---|---|---|---|
| 1 | application/vnd.rsgo.stack.manifest.v1+yaml | stack.yaml – RSGO Manifest | Yes |
| 2 | application/vnd.rsgo.stack.lock.v1+json | lock.json – Image Digests | No |
| 3 | application/vnd.rsgo.stack.meta.v1+json | meta.json – Marketplace Metadata | No |
Alternative: Standard Docker Image
Section titled “Alternative: Standard Docker Image”You can also use a standard Docker image with stack files at known paths:
/rsgo/stack.yaml # or stack.yml/rsgo/lock.json # optional/rsgo/meta.json # optionalRSGO automatically extracts these files from tar.gz layers during sync.
Lock Files
Section titled “Lock Files”A lock file pins each service image to a specific digest (sha256:...) instead of a mutable tag. This ensures deployments use the exact same image regardless of tag changes.
lock.json Format
Section titled “lock.json Format”{ "apiVersion": "1", "stackName": "my-app", "stackVersion": "1.0.0", "images": [ { "name": "web", "image": "nginx", "tag": "1.25-alpine", "digest": "sha256:a8281ce42034b078dc7d88a5bfe6cb40...", "role": "main" }, { "name": "cache", "image": "redis", "tag": "7-alpine", "digest": "sha256:e422889e156a60c4e7f0ba0c3e5b..." } ]}How Digest Resolution Works
Section titled “How Digest Resolution Works”- If a lock file exists and contains an entry for a service →
image@sha256:digest - If no lock entry exists for a service → falls back to
image:tag - Lock files are optional — deployments work without them
Publishing OCI Stack Bundles
Section titled “Publishing OCI Stack Bundles”Using a Dockerfile
Section titled “Using a Dockerfile”FROM scratchCOPY stack.yaml /rsgo/stack.yamlCOPY lock.json /rsgo/lock.jsondocker build -t ghcr.io/myorg/rsgo-stacks:v1.0.0 .docker push ghcr.io/myorg/rsgo-stacks:v1.0.0Using ORAS CLI
Section titled “Using ORAS CLI”oras push ghcr.io/myorg/rsgo-stacks:v1.0.0 \ stack.yaml:application/vnd.rsgo.stack.manifest.v1+yaml \ lock.json:application/vnd.rsgo.stack.lock.v1+jsonGitHub Actions Example
Section titled “GitHub Actions Example”name: Publish Stack Bundleon: push: tags: ['v*']
jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Log in to GHCR run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build and push bundle run: | docker build -t ghcr.io/${{ github.repository }}/stacks:${{ github.ref_name }} . docker push ghcr.io/${{ github.repository }}/stacks:${{ github.ref_name }}Caching and Sync Behavior
Section titled “Caching and Sync Behavior”- Downloaded manifests are cached in
~/.rsgo/oci-cache/{sourceId}/{tag}/ - On sync, RSGO checks the manifest digest — if unchanged, the cached version is used
- Only new or changed tags trigger a re-download
- Pagination is supported for repositories with more than 100 tags
API Reference
Section titled “API Reference”Test Connection
Section titled “Test Connection”POST /api/stack-sources/test-oci-connection| Field | Type | Required | Description |
|---|---|---|---|
registryUrl | string | Yes | Registry hostname |
repository | string | Yes | Repository path |
username | string | No | Registry username |
password | string | No | Registry password |
Response:
{ "success": true, "message": "Connection successful. Found 15 tag(s).", "tagCount": 15, "sampleTags": ["v1.0.0", "v1.1.0", "v2.0.0"]}Create OCI Registry Source
Section titled “Create OCI Registry Source”POST /api/stack-sources{ "id": "my-oci-stacks", "name": "My OCI Stacks", "type": "OciRegistry", "registryUrl": "ghcr.io", "repository": "myorg/rsgo-stacks", "tagPattern": "v*", "registryUsername": "user", "registryPassword": "token"}