Skip to Content
DocumentationTutorialsPublish your first app

Publish your first app

Take a Docker image and turn it into an entry in the Companion App Store that anyone can install in one click.

Time: ~45 minutes · You’ll need: a published Docker image, and a GitHub account

What you’ll have at the end

  • An app directory that satisfies every publication requirement
  • A pull request against CI-Marketplace 

The four required files

Every app is a directory under apps/<slug>/. All four files are required — missing or empty ones cause the app to be skipped at deploy and flagged by the audit tooling.

    • config.json
    • docker-compose.json
      • description.md
      • logo.png

Start from the template

Fork and clone CI-Marketplace , then:

cp -r apps/_template apps/my-app

The slug you choose is your app’s identity — the directory name and the id in config.json must match.

Write config.json

Ten fields are required:

FieldTypeNotes
idstringMust match the directory name
namestringHuman-readable name
versionstringSemver recommended
short_descstringOne line, 500 characters or fewer
authorstringYou, or the upstream maintainer
portnumberPrimary port; optional for no_gui and mcp entries
sourcestringURL to the source repository
categoriesstring[]e.g. ["utilities"]
availablebooleantrue → published and visible; false → ingested as a hidden draft
supported_architecturesstring[]e.g. ["arm64", "amd64"]

A minimal valid file:

{ "id": "my-app", "name": "My App", "version": "1.0.0", "short_desc": "A short description of what the app does.", "author": "your-name", "port": 8080, "source": "https://github.com/you/my-app", "categories": ["utilities"], "available": true, "supported_architectures": ["arm64", "amd64"] }

Set available: false while you’re still iterating. Your app is ingested as a draft and stays hidden from the store until you flip it.

Optional fields worth knowing: description, website, exposable, dynamic_config, form_fields, cihub_app_version, force_pull, no_gui, and mcp. See the config.json reference.

Write docker-compose.json

JSON, not YAML, on schema version 2. Exactly one service must be marked isMain.

{ "schemaVersion": 2, "services": [ { "name": "my-app", "image": "org/my-app:1.0.0", "internalPort": 8080, "isMain": true, "volumes": [ { "hostPath": "${APP_DATA_DIR}/data", "containerPath": "/data" } ], "environment": { "KEY": "value" } } ] }

Per-service properties: name, image, internalPort, isMain, volumes, environment, dependsOn, entrypoint, extraLabels. See Dynamic Compose.

Pin a real image tag rather than latest where you can. Users get whatever the tag resolves to at install time.

Add metadata

  • metadata/description.md — the long description on your app’s detail page
  • metadata/logo.png or logo.jpg — at least one, and it must not be 0 bytes

Test on a device

Install your app on a Hub and confirm it starts, serves on its port, and persists data across a restart.

This step is harder than the marketplace README implies. See Testing before you submit below — the documented local route is currently disabled in the dashboard.

Capture a verification screenshot

Save a screenshot of your app running to:

e2e/screenshots/my-app.png

This is the QA record that the app was installed and verified on a device. It is strongly recommended and surfaced by the audit tooling, but it is not currently a hard deploy gate — the pipeline ships any apps/<slug>/ with a valid config.json. Store visibility is controlled by available, not by the screenshot.

Open a pull request

Commit your directory and open a PR against CI-Marketplace. The pipeline deploys on merge.

Testing before you submit

The marketplace instructions say to install your app on a device before submitting, and other pages here have described pointing Hub at a fork via a custom app store. That route is not currently available from the dashboard.

Hub’s Settings → App Stores → Add App Store dialog is disabled. It shows “Feature coming soon” and the detail text “Custom app store setup is under maintenance. Your Hub uses the CI Marketplace automatically; no manual store URL is needed.” The source comment is explicit: manual app store URLs were a Tipi-era feature and were removed.

The backend still exposes an app-store creation endpoint, so the capability has not been deleted outright — but the supported user-facing path is gone.

Open questions for whoever owns the marketplace flow. These are why this section describes the situation instead of prescribing a workaround:

  1. How should a contributor test an app before submitting? Is there a supported route today — a debug endpoint, a CLI command, a dev-only flag — or is the honest answer “submit as available: false and test from the draft”?
  2. Is the create-app-store API still supported, or is it retained only for internal seeding? If it works, the request shape should be documented here. If it doesn’t, Create your own app store and Custom App Stores both describe a removed feature and should be retired.
  3. Is the custom-store UI coming back? The dialog says “coming soon”, which reads as temporary; the code comment reads as permanent. Those disagree.

Until they’re settled, the reliable path is submitting with available: false and testing the draft.

Next

Last updated on