Skip to Content
DocumentationContributingSubmitting an app to the Store

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:

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 cihub CLI, if you submit from the command line

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.

FilePurpose
config.jsonMetadata, form fields, categories, Hub integration — see the config.json reference
docker-compose.jsonService definition — see Dynamic Compose
metadata/description.mdLong description for the App Store detail page
metadata/logo.png or metadata/logo.jpgLogo; 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:

  • available controls store visibility. true publishes the app; false ingests it as a hidden draft.
  • port is 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 with APP_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.

RouteWeb formPull requestCLI
Whereci.computer/store-submitapps/<slug>/ in CI-Marketplace, PR against devcihub login, then cihub submit <dir>
Account neededNoneGitHub, with repository accessA CI Account organization
FilesThe four files belowThe four files, under apps/<slug>/The four files, in any folder
ReviewApproved apps appear in the App Store for all Hub usersMaintainers verify a clean install, official images, and no hardcoded secretsIngested as pending; it never publishes on its own
Recommended✓––
All three routes need the same four files and end in review.

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 test

The 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 login

cihub 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-app

The 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-app

cihub 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 random form fields for passwords and secrets
  • Declare min_hub_version if your app needs a recent Hub feature. Hub does not enforce it yet, so also describe the requirement in metadata/description.md
  • Pin image tags or digests rather than latest. Set force_pull: true only 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_architectures accurately, 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

Questions about submissions? Ask in the Discord  or see Support .

Last updated on