Submitting Apps to the Marketplace
Want your app in the Companion App Store? You can submit through our web form, open a pull request to the marketplace repository, or submit from the cihub command line.
Every route takes the same four files through review:
flowchart TD
p["Package apps/#lt;slug#gt;/<br/>config.json · docker-compose.json<br/>metadata/description.md · metadata/logo"] --> i{"Docker image?"}
i -->|"Already on Docker Hub or GHCR"| r["Reference it in<br/>docker-compose.json"]
i -->|"Static site, no image yet"| b["Static Container Builder:<br/>docker build, push to a registry"]
b --> r
r --> s{"How to submit"}
s -->|"Web form (recommended)"| f["ci.computer/store-submit"]
s -->|"Pull request"| pr["Add apps/#lt;slug#gt;/ to<br/>CI-Marketplace, open a PR against dev"]
s -->|"CLI"| cli["cihub login, then<br/>cihub submit #lt;dir#gt;"]
f --> v["Review: clean install, official images,<br/>no hardcoded secrets, sensible defaults"]
pr --> v
cli -->|"ingested as pending"| v
v -->|"approved"| a["App Store, for all Hub users"]
click p "#app-folder-structure"
click b "#build-with-static-container-builder"
click f "https://ci.computer/store-submit"
click pr "#submit-via-pull-request"
click cli "#submit-with-the-cli"Prerequisites
- The app must run as a Docker container
- A public or submit-able Docker image — or build one with the Static Container Builder
- A GitHub account with access to the CI-Marketplace repository, if you submit by pull request. The repository is private; see Contributing.
- A Companion Hub install with the
cihubCLI, if you submit from the command line
Submit via the web form (recommended)
Visit ci.computer/store-submit and complete the submission form. Have the files described below ready: a config.json manifest, a docker-compose.json, a description, and a logo.
After review, approved apps appear in the App Store for all Hub users.
App folder structure
Each app lives in apps/<slug>/ in the CI-Marketplace repository, and the folder name must match the id in config.json. Nextcloud (apps/nextcloud/) is a reference implementation, and apps/_template/ is the starting point for a new app:
- config.json
- docker-compose.json
- description.md
- logo.png
Required files
Missing or empty files cause the app to be skipped at deploy.
| File | Purpose |
|---|---|
config.json | Metadata, form fields, categories, Hub integration — see the config.json reference |
docker-compose.json | Service definition — see Dynamic Compose |
metadata/description.md | Long description for the App Store detail page |
metadata/logo.png or metadata/logo.jpg | Logo; the file must not be empty |
A metadata/screenshots/ folder for store-listing gallery images is optional. A verification screenshot at e2e/screenshots/<slug>.png in the repository is recommended, and required before an app is promoted to Verified or Featured.
config.json
Required fields are id, name, version, short_desc, author, port, source, categories, available, and supported_architectures. The validation tests also require cihub_app_version. Use cihub_app_version (not the legacy tipi_version); dynamic_config defaults to true, so you can omit it. Two fields need care:
availablecontrols store visibility.truepublishes the app;falseingests it as a hidden draft.portis a host port that Hub reserves for the app. It must be unique across the catalog, 1024 or higher, and outside the ports Hub reserves for itself (3000, 5001, 5002, 5432, 5672, 6543, 9091–9092, 9443, 9480, and 15672). A duplicate or reserved port makes installs fail withAPP_ERROR_PORT_ALREADY_IN_USE, so the validation tests reject it.
Valid categories values are network, media, development, automation, social, utilities, photography, security, featured, books, data, music, finance, gaming, ai, agents, mcp, and companion-intelligence.
{
"name": "Nextcloud",
"id": "nextcloud",
"available": true,
"short_desc": "A safe home for all your data.",
"author": "nextcloud",
"port": 8301,
"categories": ["data", "featured"],
"description": "Nextcloud is a suite of client-server software...",
"version": "34.0.3",
"source": "https://github.com/nextcloud/server",
"website": "https://nextcloud.com",
"exposable": true,
"supported_architectures": ["arm64", "amd64"],
"dynamic_config": true,
"form_fields": [
{
"type": "random",
"label": "Database Password",
"min": 32,
"env_variable": "NEXTCLOUD_DB_PASSWORD"
},
{
"type": "text",
"label": "Admin User",
"default": "admin",
"required": true,
"env_variable": "NEXTCLOUD_ADMIN_USER"
}
],
"cihub_app_version": 10
}docker-compose.json
Dynamic compose (schema version 2) is the current format. Define services, internal ports, volumes, health checks, and environment variable references:
{
"schemaVersion": 2,
"services": [
{
"name": "nextcloud",
"image": "nextcloud:latest",
"internalPort": 80,
"isMain": true,
"volumes": [
{
"hostPath": "${APP_DATA_DIR}/data/nextcloud",
"containerPath": "/var/www/html"
}
],
"environment": [
{ "key": "POSTGRES_PASSWORD", "value": "${NEXTCLOUD_DB_PASSWORD}" }
]
}
]
}See Dynamic Compose for the full schema.
metadata/description.md
Long-form description shown on the app’s detail page in the App Store. Supports Markdown/HTML.
metadata/logo.png
Square logo (recommended 256×256 px). Displayed in the App Store grid and app detail page. metadata/logo.jpg is also accepted.
Building your container
You have two paths:
Use an existing Docker image
If your app already publishes to Docker Hub or GHCR, reference that image in docker-compose.json. This is the fastest path for standard open-source apps.
Build with Static Container Builder
For static sites and front-ends without a public image, use the Static Container Builder — a Dockerfile that bakes your built assets into a hermetic nginx image:
docker build --build-arg STATIC_FILES_PATH=./dist -t my-app .Push the resulting image to a registry (Docker Hub or GHCR) and reference it in your docker-compose.json.
There is no ci-builder CLI. Earlier versions of these docs described
ci-builder init/build/package commands that do not exist — use docker build as shown above.
AI-enabled apps
If your app uses LLMs, add hub_integration.inference to map Hub-resolved AI variables into your app’s env. See AI & Inference.
Agent apps that talk to Hub MCP should set hub_integration.mcp_client: true.
Submit via pull request
Alternatively, add your app under apps/<slug>/ in CI-Marketplace and open a pull request against the dev branch, which is the repository’s default branch. Follow the same folder structure.
| Route | Web form | Pull request | CLI |
|---|---|---|---|
| Where | ci.computer/store-submit | apps/<slug>/ in CI-Marketplace, PR against dev | cihub login, then cihub submit <dir> |
| Account needed | None | GitHub, with repository access | A CI Account organization |
| Files | The four files below | The four files, under apps/<slug>/ | The four files, in any folder |
| Review | Approved apps appear in the App Store for all Hub users | Maintainers verify a clean install, official images, and no hardcoded secrets | Ingested as pending; it never publishes on its own |
| Recommended | ✓ | – | – |
Scaffold and validate
In a clone of CI-Marketplace, run pnpm run new-app to scaffold apps/<slug>/ from apps/_template, or copy the template folder yourself. Then run the validation tests:
pnpm install
pnpm testThe tests check config.json against the schema, the required files, unique and grantable ports, duplicate JSON keys, and GPU declarations. Run pnpm check-image-pins to resolve floating image tags to digests.
Test before you submit
Hub’s Add App Store dialog is disabled, so you cannot point a Hub at your fork. See Testing before you submit for the draft-listing route.
Open a PR
Include screenshots or a short screen recording of a successful install. Set supported_architectures accurately — Hub refuses to install an app on a host architecture it does not list, and it also checks that the app’s images publish that architecture.
Review
Maintainers verify the app installs cleanly, uses official images where possible, and follows security best practices (no hardcoded secrets, sensible defaults).
Submit with the CLI
The cihub CLI can submit an app folder to the catalog without a pull request:
cihub logincihub login opens a browser approval once and stores an organization developer token in ~/.config/cihub/portal-login.json. Add --device for the device-code flow over SSH. Then check the folder without pushing anything:
cihub submit --dry-run ./my-appThe dry run validates the folder and prints the catalog ID, which is <id>_<your-org-slug>, plus how each image will be rewritten. Then submit:
cihub submit ./my-appcihub submit rewrites the images in your docker-compose.json to Portal’s registry, pushes them from your local Docker, and ingests the bundle as pending. The folder needs the four required files; id must be lowercase letters, digits, and hyphens, and the images must exist in your local Docker. Submitting never publishes — staff review pending apps.
First submission? Publish your first app walks the four required files in order, and is candid about where local testing currently stands.
Guidelines
- Prefer official upstream Docker images
- Provide sensible defaults; use
randomform fields for passwords and secrets - Declare
min_hub_versionif your app needs a recent Hub feature. Hub does not enforce it yet, so also describe the requirement inmetadata/description.md - Pin image tags or digests rather than
latest. Setforce_pull: trueonly when Hub must pull the image on every start; the install dialog then warns users that they may be rate-limited by the registry - Set
supported_architecturesaccurately, and test on both amd64 and arm64 if you claim both - Test a clean install on a fresh Hub instance before submitting
- Do not bundle telemetry or phone-home behaviour without disclosure