1Panel AppStore Adapter
Adapt Docker applications into reviewable 1Panel AppStore packages, then validate their structure, metadata, Compose configuration, localization, and upgrade behavior against source-backed rules.
Submission Target and Delivery Defaults
Use third-party by default. Select official only for an official 1Panel appstore submission, and pass --submission-profile official to generation, migration, import, and validation. Read references/submission-profiles.md for the exact file and form contract before choosing a route.
- Deliver only
<out-dir>/<app-key>/. Keep source, license-review, registry, and test evidence outside it at<out-dir>/.evidence/<app-key>/source-evidence.json; pass--source-evidence <path>when validating a copied package. - Third-party packages retain
.env.sampleand reviewed path choices. Official packages omit.env.sample; any existing directory form fields keep their source-backed defaults withdisabled: trueandedit: false. Neither mode changes the selected service graph or mount mechanism. - Write Chinese
README.mdand EnglishREADME_en.mdwith product features and usage. Followreferences/readme-style.md. Do not put the selected application version, audit records, or generator diagnostics into product prose; document a necessary upgrade boundary only when it changes a user action. - Add a lifecycle hook only for a concrete setup, reconciliation, migration, or cleanup operation. Prefer Compose and upstream entrypoints for ordinary startup. Do not build an extra startup framework, runtime package installer, secret generator, or generic supervisor without an exact upstream requirement. Preserve source-backed ownership and path confinement when they are needed.
- Do not generate application license text merely because an image is referenced. Ship only notices or sources required by material actually copied into the package. The fallback icon's MIT notice is included in the README, without an extra license text or SVG.
Start Here: Choose One Route
Work from the skill directory so every scripts/... and references/... path resolves. Read this SKILL.md through its final line. First classify the input, then follow exactly one route below. Open and read every reference named by that route through its final line before running its command, and state how the references affected the plan or artifact. Every route ends at Completion Gates. If official Docker deployment evidence is unavailable, stop and report the missing evidence; do not guess a package. Publishing is outside this skill.
- New app from official Docker/Compose: Read
references/source-policy.md,references/topology-preflight.md, andreferences/lifecycle-safety.md, then record the preflight decision before scaffolding. Stop forplatform_stack_terminal; stop forspecialized_conditionaluntil every named prerequisite is proven.--out-diris a parent: for example, runbash scripts/scaffold-v2.sh --app-key <app-key> --title <title> --image <image> --version <version> --out-dir "$RUN_ROOT/artifact" --source-repository <url> --source-docker-docs <url> --source-compose-file <url>, then require"$RUN_ROOT/artifact/<app-key>"to directly containdata.yml,README.md,README_en.md, and<version>/; externalsource-evidence.jsonis a sibling sidecar; reject<app-key>/<app-key>. Review every generated file against the authoritative Compose and replace all placeholders. For callback, origin, or other public URL fields, never synthesizelocalhostor127.0.0.1: expose a required public URL as a required form field; leave an optional public URL empty or omit it and document the affected features. - AppSpec input: Read
references/appspec.md,references/source-policy.md,references/topology-preflight.md, andreferences/lifecycle-safety.md. Runpython3 scripts/generate-from-appspec.py --spec <appspec.json> --out-dir <out-dir> --validate --require-validate. Review the generated topology, variables, metadata, translations, lifecycle ledger, and validation report against the AppSpec and official sources. - Existing v1 or mixed package: Read
references/source-policy.md,references/topology-preflight.md,references/upgrade-maintenance.md, andreferences/lifecycle-safety.md. Runbash scripts/migrate-v1-to-v2.sh --src <app-dir> --out <out-dir> [--version <source-version>] [--target-version <target-version>] --source-repository <url> --source-docker-docs <url> --source-compose-file <url>. Review the migrated root/version metadata, Compose,.env.sample, lifecycle scripts, and upgrade compatibility; source URL flags may be omitted only when the source package has valid external or historical in-packagesource-evidence.json. - aaPanel/Baota input: Read
references/baota-migration-workflow.md,references/baota-app-format.md,references/baota-to-1panel-mapping.md,references/source-policy.md,references/topology-preflight.md, andreferences/lifecycle-safety.md. Precheck the complete prepared input withpython3 scripts/import-baota-app.py --input <baota-app-dir> --precheck-only --report <report.json>; for a batch add--batch. Then convert one selected version per invocation withpython3 scripts/import-baota-app.py --input <baota-app-dir> --out-dir <out-dir> --version <exact-version> --validate --require-validate. Review every output asconverted_candidateagainst official upstream evidence; never infer version order from Baota metadata. - Update an existing v2 app: Read
references/upgrade-maintenance.md,references/source-policy.md,references/topology-preflight.md, andreferences/lifecycle-safety.md. Compare the old and new package before editing; use only the needed helper commands below. Review image lineage, persisted data, changed variables, dependencies, lifecycle scripts, and direct-upgrade behavior, then run final validation. - Validate only: Read
references/source-policy.md,references/topology-preflight.md, andreferences/lifecycle-safety.md; also readreferences/upgrade-maintenance.mdwhen several versions or an update are involved. Start withbash scripts/validate-v2.sh --dir <app-dir>and review every failure and warning before strict validation. Validation does not authorize guessing or silently patching unknown semantics. - PHP runtime: Read
references/php-runtime.md,references/source-policy.md,references/topology-preflight.md, andreferences/lifecycle-safety.mdbefore choosing a generator. Follow the runtime-specific package shape, review picker metadata and actual runtime integration, then use the applicable helper commands and final validation below. Do not treat a PHP runtime as an ordinary website/tool app.
Exact Helper Commands
Use scripts for their named job instead of manually recreating their behavior. Review each diff after a mutating helper; patch helpers normalize structure but cannot prove application semantics.
| Job | Command |
|---|---|
| New v2 scaffold | bash scripts/scaffold-v2.sh --app-key <key> --title <title> --image <image> --version <version> --out-dir <out-dir> --source-repository <url> --source-docker-docs <url> --source-compose-file <url> |
| AppSpec generation | python3 scripts/generate-from-appspec.py --spec <appspec.json> --out-dir <out-dir> --validate --require-validate |
| v1 migration | bash scripts/migrate-v1-to-v2.sh --src <app-dir> --out <out-dir> [--version <source-version>] [--target-version <target-version>] --source-repository <url> --source-docker-docs <url> --source-compose-file <url> |
| Single Baota precheck | python3 scripts/import-baota-app.py --input <baota-app-dir> --precheck-only --report <report.json> |
| Batch Baota precheck | python3 scripts/import-baota-app.py --input <apphub-dir> --batch --precheck-only --report <report.json> |
| Explicit-version Baota import | python3 scripts/import-baota-app.py --input <baota-app-dir> --out-dir <out-dir> --version <exact-version> --validate --require-validate --report <report.json> |
| Root metadata patch | python3 scripts/patch_root_data_yml.py <app-dir>/data.yml [app-key] [architectures] |
| Version metadata patch | python3 scripts/patch_version_data_yml.py <app-dir>/<version>/data.yml |
| Compose patch | python3 scripts/patch_compose_yml.py <app-dir>/<version>/docker-compose.yml [app-type] |
| Regenerate env sample | bash scripts/gen-env-sample.sh <app-dir>/<version>/data.yml <app-dir>/<version>/.env.sample |
| Finalize required directory initialization | bash scripts/finalize_runtime_scripts.sh <app-dir> <app-dir>/<version> |
| Apply a proven non-root directory owner | bash scripts/finalize_runtime_scripts.sh <app-dir> <app-dir>/<version> --dir-owner APP_DATA_DIR=<uid>:<gid>:0750 --replace-init |
| Normalize logo | bash scripts/normalize-logo.sh <app-dir>/logo.png |
| Baseline validation | bash scripts/validate-v2.sh --dir <app-dir> [--version <version>] |
| Delivery validation | bash scripts/validate-v2.sh --dir <app-dir> [--version <version>] --strict-store --i18n-mode strict --source-evidence-mode required --require-delivery-evidence |
Completion Gates
- Confirm authoritative repository, Docker documentation, Compose/image evidence, license, and topology; record unsupported facts instead of inventing them, and stop when the selected preflight route says to stop.
- Preserve the selected upstream service graph, dependencies, internal networks, persistence, and security controls. Give every Compose service
labels.createdBy: "Apps"and a uniquecontainer_namebased on${CONTAINER_NAME}unless current 1Panel runtime evidence requires another shape. - Build an authoritative control inventory from the selected official launch command, Compose, image, and entrypoint before editing. Record services, image commands and users, environment variables and fixed values, healthchecks, dependencies, ports, networks, mounts and mount options, capabilities, security options, privileged mode, devices, and host namespaces. Compare the final Compose against that inventory, preserve every source-backed control, and justify every omission or change with official source or target-platform evidence. A fixed upstream hardening value is not an optional install-form setting; keep it fixed unless evidence supports changing it.
- Build a capability contract for each official deployment shape. Record its user-visible capabilities and the exact service, image, environment, and dependency combination that enables each one. Preserve the full upstream capability set by selecting either the full upstream default or an officially/target-platform-proven capability-equivalent alternative. Select a reduced-capability profile only when the user explicitly requests it; disclosure does not substitute for capability preservation. For an authorized reduced profile, prove the remaining combination is supported, list the exact unavailable features in the README and report, and use any required compatible image variant; do not call it the default or source-equivalent topology. Missing compatibility evidence blocks scaffolding.
- Before assigning any route, write a deployment-shape census. Start from official install guides and their indexes or README, then enumerate every referenced sample or Compose root in the application and companion deployment repositories. For each discovered root, trace its include/template chain and record whether it is selected or rejected and why. A route conclusion that considers only the most obvious sample is incomplete.
- After the census, evaluate each deployment shape independently in a deployment-shape decision ledger. For each shape, record its exact root and release, support scope, default capability set, external dependency ownership, persistence boundary, migration and upgrade boundary, and disposition as selected, conditional, or rejected with evidence. Treat a documented operator-managed dependency as an external-service boundary, not a missing service. A more complex production topology does not invalidate a smaller official topology, and a production-ready label is scoped evidence, not an exclusivity claim. Invalidate a smaller shape only with exact-version evidence in one of these categories: officially required, exclusive, deprecated, or capability-incomplete. Assign platform_stack_terminal only after every capability-complete official shape has been evaluated and none can enter
ordinary_candidateorspecialized_conditionalwith satisfiable named prerequisites. - The default capability set excludes optional features that are not enabled by default. Do not pre-publish optional listeners, mounts, devices, capabilities, or host permissions. Keep one only when the selected profile explicitly enables and fully configures it and the capability is validated; "for later configuration" is not evidence that it belongs in the selected profile.
- Apply the historical official source fallback before stopping for missing deployment evidence. A single 404 or moved page is not a terminal condition: inspect the exact release tree, version history, official documentation repositories, pinned historical Compose, and official image/startup sources, then record every attempted official source. When deployment evidence comes from a companion repository, pin its exact commit and trace the selected root through its actual include/template chain; an alternative file on a moving default branch is not evidence that the selected topology uses it. Stop only after those paths are exhausted and a concrete unsafe unknown remains; name the missing contract fact and the unsafe guessed decision it blocks.
- A terminal route does not waive OCI inspection. Inspect the OCI manifest and OCI config for each published image used by every viable official shape, including base, bootstrap, or runtime images named by launchers. If no immutable published registry artifact exists or registry access remains unavailable after the approved checks, record the unavailable registry fact and limit negative security or runtime claims; do not infer image controls from YAML alone.
- Build a startup configuration contract by inspecting every available exact-version authority among the official install examples, configuration reference, image defaults, entrypoint, and startup source; record unavailable authorities instead of requiring all of them to exist. Do not treat the selected official Compose as an exhaustive startup configuration list. Scope the ledger to required, startup-fatal, stability-bearing, or coupled inputs for the selected topology, plus any selected or default-dependent optional inputs. Record each value's default, empty-value rule, consumer, validation rule, secret format, stability requirement, and cross-field constraints, and prove the consumer and startup behavior from source or exact-image/runtime evidence. Reconcile every required value into the final Compose,
data.yml,.env.sample, or source-backed lifecycle logic, then trace it to the application consumer. Whenever a formenvKey,label, ortypechanges, re-review itsdefault,rule, andvalues; keep every non-empty, non-secret, Compose-consumed form default consistent with.env.sampleas defined inreferences/lifecycle-safety.md. Treat validated form values as literal data during template generation, never as regular-expression or replacement-program input.required: trueor a genericparamComplexityrule does not prove the application-specific validation contract; enforce the full rule before Compose starts or stop. A successful Compose render does not prove that the panel default passes application startup validation; an unresolved or omitted required value blocks a pass claim. - Record
source-evidence.json.images[]for every selected version's Compose service image, including database, cache, browser, migration, and helper services. Bind the version, exact service, resolved image reference, and matching registry digest; prefer a digest-pinned runtime reference, but do not claim that a tag-only reference is immutable. The legacy singleimageEvidencecovers only a one-image Compose. For a multi-platform image, record the index digest separately from each platform child digest. Associate a platform child digest only from that same registry descriptor; do not infer it from descriptor order, the current host architecture, or a separate query. Recheck every declared platform-to-digest mapping before reporting it. - For every service with a writable bind, add
runtimeIdentityto its selectedimages[]entry (or legacyimageEvidence) with numericstartupUid,startupGid,steadyStateUid,steadyStateGid, an exact-version HTTPSsource, andwritableBindOwner. Usehost-initonly with generated--dir-owner/--fixed-dir-ownercoverage for every bind,image-managedonly with a separate HTTPSownerEvidence, androot-runtimeonly whensteadyStateUidis0. Delivery validation rejects missing identity evidence or an unmatched non-root owner plan. - Keep a minimal install form: expose only settings users need for the selected default topology. Do not mirror every optional upstream profile or environment variable; remove disabled profiles or resolve reviewed package defaults while maintaining Compose/form/
.env.sampleclosure. - For callback, origin, and externally visible base URLs, use the exact current upstream variable and expose one full public URL when user input is required. Do not reconstruct it from host, protocol, or port fields unless version-matched official evidence proves that contract equivalent. Keep the external URL separate from the internal listener: for a reverse proxy, preserve the documented internal scheme/port, TLS termination boundary, and trusted-proxy or hop settings. Do not set an internal listener to HTTPS without the application's required certificates and source-backed configuration.
- Treat
.envas untrusted data in lifecycle scripts. Neversourceorevalit; parse only exact known keys, strip quotes, validate values, and resolve relative paths from the app root. - Complete the
references/lifecycle-safety.mdpath and mount ledger, including each source-backed mount mechanism, mount options, and operator-access contract. Treat source, target, read/write mode, propagation, and security options in authoritative Compose as required delivery defaults; preserve them exactly unless target-platform evidence proves incompatibility and a reviewed replacement preserves the same contract. A named volume is not a drop-in replacement for a configuration bind used by official host-side editing, backup, restore, or support procedures; preserve that fixed package-local bind and do not add anAPP_DATA_DIRform unless a selectable host path is actually required. Derive startup and steady-state runtime UID/GID separately from the published OCI configuration, Composeuser, and verified entrypoint/process behavior; keep mutable host paths package-local and confined before creation, permission changes, or cleanup. When the selected authoritative deployment uses a named volume for a non-root writable path, preserve that volume. When it uses a bind, use the explicit--dir-owner/--fixed-dir-ownerhelper with a source-backed identity on a direct child of the trusted version root, then verify the exact owner, mode, and a write probe as that identity. Change the mechanism only through the same target-platform incompatibility and equivalent-replacement gate. A plain root-created0755bind directory blocks delivery. Do not recursively change ownership on an unconfined or symlinked path. - Report an observed owner or mode as an observation tied to the invoking UID/GID and umask. Claim a portable guarantee only when the delivered lifecycle script explicitly enforces that owner or mode and the exact-artifact test verifies it.
- Create or validate the exact source file for every file bind before Compose starts. Prove each generated secret format against the application contract, keep stable secrets across upgrade, URL-encode URL credentials, and apply the official escaping rules to every other connection-string grammar.
- Replace placeholders with real product metadata and meaningful translations in all required locales. English fields must contain English. Record the application and asset licenses; when exact redistribution terms require attribution, copyright or license text, source disclosure, or NOTICE delivery, include that required material in the package instead of relying on a link. When a license has material use restrictions, name and link it in the README instead of keeping it only in machine-readable evidence. Verify an asset's redistribution basis separately from the application code license; for an unresolved asset license or trademark permission, use the neutral placeholder immediately rather than shipping the asset with a future-confirmation note.
- Render and validate the exact delivered artifact without creating then removing a file it needs. Keep only lifecycle hooks that perform required work, and retain executable mode for every delivered hook. Missing no-op hooks are valid; preserve any source-backed setup, upgrade, or cleanup operation. Run baseline validation first, then
bash scripts/validate-v2.sh --dir <app-dir> [--version <version>] --strict-store --i18n-mode strict --source-evidence-mode required --require-delivery-evidence; unresolved failures block a pass claim. - Before finalizing the README or report, build a configuration claim ledger and compare each statement with the exact Compose,
data.yml,.env.sample, and lifecycle scripts. Classify every described value as fixed, defaulted, generated, optional, or user-configurable. If an editable form field controls it, describe it as user-configurable with that default rather than fixed; contradictions block a delivery-ready claim. - For every form field, record its install-time and steady-state consumer before choosing
edit. A value generated once, written into persistent configuration, or identity-bearing must useedit: falseunless an idempotent reconciliation or migration applies later edits to the real persisted consumer. Do not expose a control that only changes.envwhile the application continues using an older persisted value. - Test in a real 1Panel development/test instance: clean install, application-specific readiness, restart, upgrade when applicable, uninstall, and task-owned cleanup. Report artifact paths, evidence, checks, risk-bearing permissions, assumptions, warnings, and every unexecuted runtime gate; static validation or HTTP 200 alone is insufficient. Distinguish files in the delivered AppStore package from run-only evidence caches. Do not claim that a run-only cache path is present in the delivered package.
Rule Priority
Judge every rule in this order:
- Runtime and source-code hard rules from
1Panel-dev/1Panel - Official 1Panel wiki and official docs
- Official appstore repository conventions
- External references and practical articles
Only rules backed by runtime behavior or explicit official documentation should block generation or validation. Repository conventions are guidance unless validator mode explicitly upgrades them.
Source Policy (Evidence First)
Before adapting a new candidate, read references/source-policy.md, references/topology-preflight.md, and references/lifecycle-safety.md. Complete the topology decision before scaffolding or deployment testing: ordinary candidates may proceed, specialized conditional candidates need their recorded prerequisites satisfied, and platform-stack terminal candidates stop unless the user opens a separately scoped project.
Collect evidence according to source priority:
- User-provided official repository/documentation > official image docs > official appstore structure facts > third-party examples
- Without official Docker evidence, stop expanding; do not guess images, ports, volumes, UID/GID, or dependency injection methods
- When using third-party images, user must explicitly accept, and record source and risk in delivery notes
- Preserve high-risk runtime permissions when official compose, documentation, or source evidence shows that a core application feature requires them. This includes Docker or Podman Socket mounts,
privileged: true,cap_add, host networking, host PID/IPC namespaces, device mappings, and relaxedsecurity_optsettings. Do not remove a required permission merely to reduce scanner findings. - Keep required high-risk access no broader than upstream needs, test the feature that depends on it, and document the exact permission, feature dependency, and host impact in the app README and delivery notes. A risk label is required; deletion is not the default remediation.
For existing app updates, version additions, image lineage changes, dependency changes, volume/env rewrites, or lifecycle script edits, also read references/upgrade-maintenance.md before changing files. Treat upgrade safety as part of the adaptation contract, not a post-submit note.
For PHP runtime work, especially when converting a historical package such as php-unofficial into a real 1Panel PHP runtime, also read references/php-runtime.md. PHP runtimes are not packaged like ordinary website/tool apps, and the runtime picker behavior is source-backed rather than guessable from generic appstore patterns.
Recommended Directory Structure (Store-Aligned)
Field Hierarchy Constraints (Aligned with Official apps/ Facts)
Note: In the official repository (1Panel-dev/appstore dev branch), the
data.ymlfield hierarchy under v2 structure is very stable; this skill'svalidate-v2.sh --strict-storeperforms strict validation according to this.
- Language codes: Application metadata uses runtime keys
zh-hant,pt-br, andes-es. UI locale names such aszh-Hantare accepted as historical input aliases, then normalized for generated output. See the pinned source evidence inreferences/1panel-sources.md.
Application-level: apps/<app>/data.yml
- Top-level only allows:
name/tags/title/description/additionalProperties - Top-level
typeappearing: WARN only (historical/non-standard field; official regular write doesn't place at top level) architectures: Must be inadditionalProperties.architectures(100% present in official samples)
strict-store additionalProperties required (missing = FAIL):
key/name/tags/type/website/document/architecturesgithub/shortDescZh/shortDescEn/crossVersionUpdate/limit- (Others like
recommend/description/memoryRequiredcan maintain WARN by occurrence rate)
Version-level: apps/<app>/<ver|latest|stable>/data.yml
- Top-level only allows:
additionalProperties- Can be
nullorobject(but under strict-store, recommend object with required fields filled)
- Can be
formFieldsmust be inadditionalProperties.formFields(top-levelformFieldsnot allowed)- Prohibit
architectures: Version-leveldata.yml(same level asdocker-compose.yml) should not havearchitectures(neither top-level nor insideadditionalProperties)
Convention supplement (this skill's default artifact style):
- root
data.ymltop-leveldescriptionuses single-line string (not map).- root
data.ymladditionalProperties.shortDescusesshortDescZh/shortDescEn(not map).additionalProperties.descriptionuses i18n map, complete all 12 supported languages for delivery:en/zh/zh-hant/ja/ko/ru/ms/pt-br/tr/es-es/fa/lo.- root
data.ymluses hierarchical structure: top-leveltagsandadditionalProperties.tagsboth exist and are semantically consistent (allow redundant expression).- Descriptive summaries:
titlemay be the product name. Top-leveldescriptionandadditionalProperties.shortDescZh/shortDescEnmust explain the application's purpose in a short sentence; a product name alone fails strict-store validation. Keep their meaning consistent with the multilingual description.patch_root_data_yml.pyandappstore_i18n.py --normalizecan repair missing/name-only summaries from supplied Chinese and English descriptions while preserving valid prose.- Translation constraint (strong constraint):
additionalProperties.descriptionmust be the multilingual translation of the aboveshortDescZh(not repetition of project name/title).architectures(only rootdata.yml) represents Docker image supported architecture list; should be inadditionalProperties.architectures, using hierarchical array (e.g.,- amd64/- arm64); if cannot reliably obtain (e.g., no manifest info/offline), default only fill- amd64.
formFields Structure Facts (version-level)
Location: additionalProperties.formFields: [ ... ]
formFields[] item common fields
envKey(required)type(required)required(required)default(common)rule(optional; common set below)labelZh/labelEn(high-frequency; recommend providing both)label(multi-language map, high-frequency)child(appears whentype: apps)
formFields[].type allowed set (based on official dev/apps real samples)
numberpasswordselecttextappsservice
Notes:
- Official repo has both patterns:
type: apps+child.type: service- Direct
type: service
- Therefore validator should allow
serviceinformFields[].type, but adapted artifacts can preferapps + child.serviceto express "dependency selection → service instance selection" two-step semantics.
rule common set (for this skill validation)
paramPort(most common, forPANEL_APP_PORT_*)paramExtUrlparamCommonparamComplexity
Treat this as an allowed set, not a requirement to populate rule. Normally only externally published port fields use rule: paramPort; omit rule from secrets, URLs, selectors, domains, and ordinary text unless a demonstrated 1Panel parser or application-format requirement needs it.
formFields[].edit (consumer-backed editability)
Based on official v2 app library docker-compose.yml and same-directory version-level data.yml actual write:
editis bool in DTO (default equivalent to false), but official library explicitly writesedit: truefor many fields.- Choose
editfrom the value lifecycle, not from a blanket editable default:- Use
edit: trueonly when the steady-state consumer reads the changed value directly, or an idempotent reconciliation or migration safely applies it to persisted state. - Use
edit: falsefor a value generated once, identity-bearing configuration, panel injection, or any value whose persisted consumer is intentionally not regenerated. Typical injection keys includePANEL_DB_*,PANEL_REDIS_*, andPANEL_MINIO_*. - Dependency selection fields (
type: apps/service) may useedit: trueonly when the linked-resource lifecycle supports changing the selection; enumeration alone is not migration evidence.
- Use
- Validation strategy:
validate-v2.sh --strict-storewill WARN when a required non-selector field lacks an expliciteditdecision (not FAIL, to avoid rejecting official historical output). Resolve the warning from the consumer lifecycle; do not silence it mechanically withedit: true.
Database / Redis Dependency Injection (Panel Fixed envKey)
Before preserving an upstream-bundled database/cache sidecar, inspect the target appstore and a current 1Panel store for reusable runtime applications and service registration. When a compatible 1Panel-managed runtime exists and the application supports an external service, prefer the panel runtime selector path and panel fixed envKeys; do not wait for a later review reminder to replace the bundled dependency.
Dependency preference order:
- A compatible, selectable 1Panel-managed runtime exposed through
/apps/services/<key>and the corresponding resource records. - A documented external service configuration when no reusable panel runtime is available.
- The upstream-bundled database/cache sidecar only when the runtime selector path is unavailable, unregistered, incompatible, or the upstream application requires the bundled topology.
For applications supporting multiple database engines, keep one app key and expose the engines that have independent panel/runtime evidence through one type: apps plus child.type: service selector. Map engine-specific ports through selector params. Do not create database-specific app keys or version directories merely to represent engine choice, and do not advertise an engine that only passes static compose validation.
Common fixed envKeys (recommend using as needed):
- Database:
PANEL_DB_TYPE,PANEL_DB_HOST,PANEL_DB_NAME,PANEL_DB_USER,PANEL_DB_USER_PASSWORD - Redis:
REDIS_HOST,REDIS_PORT,PANEL_REDIS_ROOT_PASSWORD,REDIS_DB
If users are expected to choose a reusable 1Panel-managed dependency from the install UI, do not leave the dependency host field as plain type: text.
- For database-family selectors, prefer
type: appspluschild.type: service. - For single-step selectors such as Redis service reuse, prefer
type: servicewith the dependencykey(for examplekey: redis) on the host envKey field. - A package that only accepts manual host input is not equivalent to a package whose UI can actually select a store/local dependency app.
Scaffold supports optional injection template:
- When running
scripts/scaffold-v2.sh, add--with-panel-deps(or alias--with-panel-db-redis), will automatically add above DB/Redis related formFields in generated<version>/data.yml(includinglabelEn/labelZh+ alabelmap using runtime locale keys).
Key points for adaptation:
- Treat store-runtime discovery as an adaptation preflight step: search existing app definitions for the dependency key, query the panel store metadata, and verify that an installed instance appears in
/apps/services/<key>before deciding the final service topology. - Treat the selector value as a 1Panel app key, not a display-label alias.
mysqlandlocalmysqlare distinct values and must be verified independently through/apps/services/mysqland/apps/services/localmysql; do not advertise one because the other works. version/data.ymlusestype: apps+child.type: serviceto injectPANEL_DB_HOST(reference 1Panel store app common dependency injection pattern).- Keep service enumeration separate from database lifecycle integration. A running option returned by
/apps/services/<key>proves the selector can enumerate that instance, but not that 1Panel created or linked an application database. When the form also requestsPANEL_DB_NAME,PANEL_DB_USER, andPANEL_DB_USER_PASSWORD, require install and upgrade evidence that the host envKey is present in theservicespayload and the installed app reportslinkDB: trueor the expectedresourceKeys; then verify schema/user creation and cleanup behavior separately. - For packages that require a runtime administrator password to initialize several upstream-defined schemas, use the actual password of the selected installed runtime. A form default is only a template and must not be treated as the current runtime credential. Do not change a shared runtime's authentication mode to make a smoke test pass.
- App uninstall does not universally own external database data. Confirm whether selector-created linked resources are removed by 1Panel, and explicitly clean only task-owned schemas/users during tests. Preserve manually managed external databases unless the user separately authorizes their removal.
- When converting an existing package from manual host input to a selector-backed dependency, keep the effective runtime envKey stable when possible. For example, changing
REDIS_HOSTfromtype: texttotype: serviceis usually upgrade-safe because existing.envvalues still map to the same compose/app variable. - If the selector conversion requires a renamed envKey or adds a new selector-driving field such as
PANEL_DB_TYPE, treat that as an upgrade migration item and backfill it inscripts/upgrade.shwhen possible. - PostgreSQL-only rule: if the app relies on panel-side PostgreSQL provisioning (
CreateDatabasein install task logs), runtime validation should use a real 1Panel-installed PostgreSQL app in the same panel. Pointing the service field at an arbitrary external hostname can bypass the intended provisioning path and create misleading failures. - For that PostgreSQL-only path, keep the application PostgreSQL user (
PANEL_DB_USER) distinct from the PostgreSQL service admin/root account. Reusing the admin username can make a correct package fail during install withUser already exists. - Do not automatically generalize those PostgreSQL-specific behaviors to MySQL; verify MySQL-linked adaptations from their own 1Panel task/runtime evidence before carrying the rule over.
- For format-sensitive secrets, do not assume a generic random password is a valid application value. Examples: Laravel
APP_KEYexpects Laravel-compatible key material, while MastodonACTIVE_RECORD_*,SECRET_KEY_BASE,OTP_SECRET, andVAPID_*have upstream-specific generator commands and formats. - When official docs expose a generator helper, prefer
scripts/init.sh/scripts/upgrade.shto generate or normalize those values from the official image/helper command instead of shipping a fixed sample secret indata.ymlor trusting a generic panel-generated random string. - If
scripts/init.shorscripts/upgrade.shreplaces a panel-provided secret with a normalized/generated value, persist that final value under the app's configurable data path and restore it during later upgrades. Real 1Panel upgrades can replay the original install form value instead of the mutated.env, which can break apps that silently rotateAPP_KEY,DB_PASSWORD, or similar persisted secrets. - Keep that secret-persistence rule distinct from the PostgreSQL-only provisioning notes above: the replay problem can affect MySQL-, PostgreSQL-, or non-DB secret fields, even though the dependency-provisioning behavior is not shared across engines.
- When simplifying forms, retain documented security controls such as user registration, anonymous access, and administrator exposure, or provide verified configuration instructions with the effective default. Review the before/after behavior so removing a switch does not silently inherit a more permissive upstream default.
- If the compose uses
network_mode: host, its listener occupies the host port directly even without aports:block. Keep the install-form envKey asPANEL_APP_PORT_*so 1Panel performs its port-occupation check, map the upstream listener variable from that key (for example,NETDATA_LISTENER_PORT=${PANEL_APP_PORT_HTTP}), and verify the application consumes it. A disabled/fixed port field must still match the real built-in listener instead of being treated as a free-to-randomize published port. - For host-network adaptations tested from a containerized smoke runner, runtime probing may need the Docker host gateway (or another host-reachable address) rather than
127.0.0.1inside the panel container. docker-compose.ymlif application usesDATABASE_*variables, need to map in compose:DATABASE_HOST: ${PANEL_DB_HOST}DATABASE_USER: ${PANEL_DB_USER}DATABASE_PASSWORD: ${PANEL_DB_USER_PASSWORD}DATABASE_DBNAME: ${PANEL_DB_NAME}
- Redis password similarly:
REDIS_PASSWORD: ${PANEL_REDIS_ROOT_PASSWORD}(if application field name differs, map as needed). - Validation must prove the selector path, not only connectivity. A smoke/install report should show the d
…(truncated)