Announcement

The Panelica Docker Template Catalog Is Now Open Source

Back to Blog
Managing servers the hard way? Panelica gives you isolated hosting, built-in Docker and AI-assisted management.
Start free

Every app in Panelica's Docker Manager App Store, from n8n to Chatwoot to Nextcloud, is defined by a single JSON file. As of today, the repository that holds every one of those files is public, MIT-licensed, and open for contributions. Panelica ships it, and now anyone can help build it.

Repository: the catalog lives at Panelica/docker-templates on GitHub. Fork it, add a JSON file, open a pull request. If it passes review, your app ships to every Panelica server with the next panel release, and your name is on it.

Why we're opening the catalog

Self-hosting grows by sharing, not by gatekeeping. Look at the catalogs people actually trust: CapRover's one-click-apps repo, Runtipi's app store, Unraid's Community Applications. None of those are maintained by a closed team guessing what users want next. They're maintained by the users themselves, one pull request at a time, and the result is catalogs that grow faster and cover more ground than any single company could manage alone.

Panelica's Docker Manager is container-first by design. We already treat every app as data, not code: a JSON description of an image, its ports, its environment variables, its volumes. That shape was already portable. Opening the repository was less a rewrite and more removing a wall that didn't need to be there.

How a template gets from your fork to a customer's server

Nothing in this pipeline lets a pull request touch a live server directly. Here is the full path a template takes:

templates/<slug>.json          you write this (one file per app)
        |
        v
CI validation                  JSON Schema check + security lint + image existence check
        |
        v
Panelica team review + merge   a maintainer reads every template before it merges
        |
        v
Release pipeline               templates are embedded into the next panelica-server build
        |
        v
Every Panelica server          the app appears in Docker Manager, App Store, after the update

A merged template does not go live immediately and does not go live selectively. It ships embedded in the panel binary, the same way every other feature does, through the regular update channel. There is no side door where a contributor's code runs on a customer's box before a Panelica engineer has looked at it.

From simple to complex, without the JSON getting scarier

The bar for a first contribution is low on purpose. A single-container app is a small file. Here is the shape of the n8n template that ships in the panel today, trimmed to the fields that matter:

{
  "slug": "n8n",
  "name": "n8n",
  "description": "Workflow automation tool",
  "image": "n8nio/n8n",
  "default_tag": "latest",
  "categories": ["automation", "devtools"],
  "ports": [
    { "host": 5678, "container": 5678, "protocol": "tcp", "description": "Web UI" }
  ],
  "env_vars": [
    { "key": "N8N_BASIC_AUTH_PASSWORD", "default": "", "required": true, "secret": true }
  ],
  "volumes": [
    { "source": "{{slug}}_data", "target": "/home/node/.n8n" }
  ],
  "min_memory_mb": 256
}

That is close to the entire file. One image, one port, one volume, one secret that Panelica generates for you. If your app fits in a single container, your template will look a lot like this.

Real applications are rarely one container, though. They need a database, a cache, sometimes a background worker. Panelica's schema handles that with a linked_services array, one entry per sidecar, and the complexity does not compound the way you might expect. Chatwoot, the open-source customer engagement platform, ships as four containers: a Rails web app, PostgreSQL with the pgvector extension, Redis, and a Sidekiq worker. The template for that whole stack still reads top to bottom:

{
  "slug": "chatwoot",
  "image": "chatwoot/chatwoot",
  "env_vars": [
    { "key": "POSTGRES_HOST", "default": "{{slug}}-db" },
    { "key": "POSTGRES_PASSWORD", "default": "", "secret": true },
    { "key": "REDIS_URL", "default": "redis://{{slug}}-cache:6379" }
  ],
  "linked_services": [
    { "name_suffix": "db", "image": "pgvector/pgvector:pg16",
      "env_vars": [ { "key": "POSTGRES_PASSWORD", "from_env": "POSTGRES_PASSWORD" } ] },
    { "name_suffix": "cache", "image": "redis:alpine" },
    { "name_suffix": "sidekiq", "image": "chatwoot/chatwoot",
      "command": "[\"docker/entrypoints/rails.sh\", \"sh\", \"-c\", \"bundle exec sidekiq\"]" }
  ]
}

Each sidecar is reachable by hostname (chatwoot-db, chatwoot-cache) from every other container in the stack, and the database picks up the exact same auto-generated password the app got, via from_env, so nobody has to hardcode a secret in two places. If you have ever written a docker-compose.yml, you already know how to write this.

Security is enforced in CI, not left to the honor system

Opening a repository to outside contributions only works if the blast radius of a bad or malicious pull request is zero. The schema and the linter make several classes of mistake structurally impossible rather than merely discouraged:

  • No privileged mode, no host PID or network namespace, no device passthrough — these fields don't exist in the schema. There's nothing to set.
  • No host path mounts. Only named volumes, and they must be prefixed with the app's own slug. A template can't reach into /etc, /proc, or the Docker socket.
  • Data services bind to localhost by default. Databases, caches, and queues never get a public port without the server owner explicitly confirming it in the panel first.
  • Secrets are generated, never hardcoded. A field marked secret: true is required to ship with an empty default; Panelica fills it with a strong random value at deploy time.
  • Images come from a fixed allowlist of registries — Docker Hub, GHCR, Quay, LinuxServer.io, Amazon ECR Public, GitLab Registry. Nothing else resolves.

All five of these are checked automatically on every pull request, before a human ever looks at the diff. Review adds the parts a linter can't: does the app actually boot to a working first-run screen, is the memory estimate honest, do the post-install notes answer the first question a new user will have.

Contribute your first template

The full walkthrough, the field reference, and three example templates covering the single-container, app-plus-database, and full-stack shapes are all in the repository's README. The short version: fork the repo, copy the closest example, rename it to your app's slug, validate it locally with the included Python script, and open a pull request. Add "contributed_by": "your-github-username" and your name shows up on the app card in every Panelica panel that runs it.

Most contributors go from fork to open pull request in under ten minutes.

What's next

The catalog that ships in Panelica today is larger than what's in the public repository right now; we're migrating the rest of it over in batches as we bring each template's documentation and test coverage up to the same bar we're asking contributors to meet. If there's an app you want to see in the App Store, or a field the schema doesn't support yet, open an issue on the repository. If the capability is safe to generalize, we'll extend the schema and the engine together.

Security-first hosting panel

Run your servers on a modern panel.

Panelica is a modern, security-first hosting panel — isolated services, built-in Docker and AI-assisted management, with one-click migration from any panel.

Zero-downtime migration Fully isolated services Cancel anytime
Share:
Built in Go.