1ctl marketplace
1ctl marketplace lets you deploy production-ready applications from a curated catalog with a single command. Each entry in the catalog is a template that encodes a container image, default resource requirements, required environment variables, ports, and volume configuration. You provide a name and optional overrides; the platform handles the Kubernetes plumbing.
you → 1ctl marketplace deploy <id> --name my-db → POST /v1/cli/marketplace/{id}/deploy → Deployment + Service + HTTPRouteAliases. 1ctl market and 1ctl apps are equivalent to 1ctl marketplace. All subcommands work the same under any alias.
Mental model
Section titled “Mental model”Marketplace templates are stored in the marketplace_apps table. Each template defines:
- The container image and tag
- Default CPU and memory requests
- Required and optional environment variables (some injected automatically via Infisical for default credentials)
- Exposed ports and readiness probe configuration
- Volume mount paths and storage requirements
When you run 1ctl marketplace deploy, the template defaults are merged with any flags you supply — your flags always win. The result is a deployment-backed workload that is then managed through the normal deployment lifecycle.
The intended product boundary is:
| Concern | Long-term home | Why |
|---|---|---|
| Browse curated applications | 1ctl marketplace | Marketplace is a catalog and discovery surface. |
| Inspect template defaults and required inputs | 1ctl marketplace get | Users need to understand what they are about to instantiate. |
| Create a workload from a curated template | 1ctl marketplace deploy today; likely also 1ctl deploy --template ... later | Template selection is an input to deployment creation, not a separate runtime species. |
| Operate the running workload after creation | 1ctl deploy, 1ctl logs, 1ctl domains, 1ctl secrets | Once created, a marketplace workload should behave like every other deployment. |
In other words:
marketplace = catalog / template discovery / install intentdeploy = canonical runtime lifecycleThis is the direction to preserve over time. Marketplace should remain a first-class noun for discovery, but Satusky should avoid growing a second forever-diverging deployment system beside 1ctl deploy.
Current implementation versus intended model
Section titled “Current implementation versus intended model”Today, the backend already treats marketplace workloads as deployments underneath, but it still reaches that result through a separate marketplace deployment path:
MarketplaceApp template ↓POST /marketplaces/deploy/create/:namespace/:marketplaceId ↓MarketplaceDeploymentService ↓Deployment + Service + Ingress records ↓Kubernetes resourcesThe marketplace backend also contains app-specific orchestration branches for workloads such as CNPG Postgres and NATS. That is useful: some templates are richer than “one image plus one Service.” But it does not require marketplace workloads to become a separate lifecycle. A good template system may produce one deployment, several resources, or operator-managed infrastructure while still handing the resulting workload back to the ordinary deployment control plane.
The long-term target is:
MarketplaceApp template ↓ resolve defaults + validate inputsDeploymentSpec / workload spec ↓shared deployment pipeline ↓normal deployment lifecycleThis keeps one place for rollout behavior, machine placement, domains, secrets, status, metrics, restart, rollback, and destroy semantics.
Why not fully merge marketplace into deploy?
Section titled “Why not fully merge marketplace into deploy?”Because the nouns answer different user questions:
| User question | Best command |
|---|---|
| “What curated apps can I install?” | 1ctl marketplace list |
| “What does this PostgreSQL template require?” | 1ctl marketplace get postgres-16 |
| “Create a workload from this curated template.” | 1ctl marketplace deploy postgres-16 ... or future 1ctl deploy --template postgres-16 ... |
| “What is running, is it healthy, and how do I operate it?” | 1ctl deploy ... |
The best long-term shape is therefore separate catalog, unified lifecycle — not two completely separate products, and not one giant command group that hides the existence of curated templates.
Why this is a strong default
Section titled “Why this is a strong default”Keeping marketplace as a catalog while converging runtime management onto deployments gives Satusky several durable advantages:
- one placement model for both custom and template-backed workloads
- one domains model
- one monitoring and status model
- one restart / rollback / destroy model
- fewer duplicate flags and fewer chances for behavior drift
- freedom for templates to become richer later without forcing users to learn a second operational language
This also pairs cleanly with the planned machine-label strategy: a template-backed workload should eventually accept the same machine selectors as any other deployment, instead of inventing marketplace-only placement rules.
External patterns worth learning from
Section titled “External patterns worth learning from”Other platforms generally separate template discovery from runtime ownership:
| Platform | Observed pattern | Lesson for Satusky |
|---|---|---|
| Fly.io | fly launch helps create/configure an app, while fly deploy is the durable deploy/redeploy lifecycle. | Creation sugar can exist without creating a second runtime model. |
| DigitalOcean Marketplace | 1-Click Apps are curated inputs used to create underlying Droplets or Kubernetes apps. | Marketplace entries can remain catalog artifacts rather than permanent workload types. |
| Railway | Templates are discoverable as their own concept, but the CLI exposes railway deploy --template <code>. | A template can be first-class for discovery while still entering through the deployment path. |
Relevant references:
- Fly Launch
- Deploy an app on Fly.io
- DigitalOcean Marketplace Droplet 1-Click Apps
- Railway templates
- Railway deploy templates
When this model should be revisited
Section titled “When this model should be revisited”If Satusky later supports products that are genuinely higher-order than a deployment — for example, a multi-service WordPress stack with a database, cache, worker, scheduled jobs, and lifecycle coupling — the right new primitive may be app or stack. Even then, marketplace should still remain the catalog that instantiates the object; it should not become the permanent runtime home for everything installed from a template.
Commands
Section titled “Commands”1ctl marketplace list
Section titled “1ctl marketplace list”1ctl marketplace list [--limit <n>] [--offset <n>] [--sort <field>]Browse the catalog of available applications.
| Flag | Default | Description |
|---|---|---|
--limit | 20 | Maximum number of results to return. |
--offset | 0 | Pagination offset. |
--sort | name | Sort field. Accepts name or downloads. |
Backend: GET /v1/cli/marketplace
Example output:
ID NAME CATEGORY CPU MEMORY DESCRIPTIONpostgres-16 PostgreSQL 16 Database 0.5 512Mi Relational databasemysql-8 MySQL 8 Database 0.5 512Mi Relational databaseredis-7 Redis 7 Cache 0.25 256Mi In-memory data storerabbitmq-3 RabbitMQ 3 Messaging 0.5 512Mi Message brokergrafana-10 Grafana 10 Monitoring 0.5 512Mi Observability dashboardprometheus Prometheus Monitoring 1.0 1Gi Metrics collectionwordpress-6 WordPress 6 CMS 0.5 512Mi Content management system1ctl marketplace get
Section titled “1ctl marketplace get”1ctl marketplace get <marketplace-id>Returns the full details of a template: description, all available configuration options, default resource requirements, and any environment variables the template exposes.
Backend: GET /v1/cli/marketplace/{id}
1ctl marketplace get postgres-16Example output:
ID: postgres-16Name: PostgreSQL 16Category: DatabaseDescription: Production-ready PostgreSQL 16 with persistent storage.
Default resources: CPU: 0.5 cores Memory: 512Mi Storage: 10Gi
Environment variables: POSTGRES_DB (auto-generated) POSTGRES_USER (auto-generated) POSTGRES_PASSWORD (auto-generated via Infisical)
Ports: 5432/tcp (PostgreSQL)
Storage: Mount: /var/lib/postgresql/data Class: default1ctl marketplace deploy
Section titled “1ctl marketplace deploy”1ctl marketplace deploy <marketplace-id> --name <name> [options]Deploy a marketplace application. Template defaults are applied for any flag you do not supply.
| Flag | Default | Description |
|---|---|---|
--name | required | Name for your deployment. Must be unique within your namespace. |
--hostname | auto-generated | Subdomain under .satusky.app to assign to this deployment. |
--cpu | template default | CPU cores to allocate. Overrides the template default. |
--memory | template default | Memory limit. Overrides the template default. |
--domain | none | Attach a custom domain. |
--storage-size | template default | Persistent volume size, e.g. "10Gi". |
--storage-class | default | Kubernetes StorageClass for the persistent volume. |
--multicluster | false | Deploy across multiple clusters. |
--multicluster-mode | active-passive | Traffic distribution: active-active or active-passive. |
Backend: POST /v1/cli/marketplace/{id}/deploy
The backend merges your flags with the template spec, then creates a Kubernetes Deployment, Service, HTTPRoute, and PersistentVolumeClaim (if the template requires storage). An Issuer is created if a domain is attached.
Current status. This command is the marketplace-specific creation path implemented today. It is useful and should remain user-friendly, but it should not be allowed to drift into a separate deployment lifecycle from 1ctl deploy.
Examples
Section titled “Examples”# Browse the catalog1ctl marketplace list
# Sort by most-deployed apps1ctl marketplace list --sort downloads
# Inspect the PostgreSQL template before deploying1ctl marketplace get postgres-16
# Deploy PostgreSQL with a larger disk and more memory1ctl marketplace deploy postgres-16 \ --name my-db \ --storage-size 20Gi \ --cpu 1 \ --memory 1Gi
# Deploy Redis with the minimum resources needed1ctl marketplace deploy redis-7 \ --name my-cache \ --memory 256Mi
# Deploy WordPress with a custom domain1ctl marketplace deploy wordpress-6 \ --name company-blog \ --domain blog.acme.com \ --storage-size 5Gi
# Deploy RabbitMQ across two clusters in active-active mode1ctl marketplace deploy rabbitmq-3 \ --name message-broker \ --multicluster \ --multicluster-mode active-activeAfter a successful deploy, the CLI prints the deployment ID, assigned hostname, and initial pod status:
==> Deploying postgres-16 as my-db
==> Created resources Deployment: my-db Service: my-db HTTPRoute: my-db.org-acme-prod.satusky.app Volume: my-db-pvc (20Gi)
==> Deployment created ID: dep_01jbx9p3q4 URL: https://my-db.org-acme-prod.satusky.app Port: 5432
Monitor status: 1ctl deploy status --deployment-id dep_01jbx9p3q4After deploying
Section titled “After deploying”Marketplace deployments are first-class Satusky deployments. Once created, manage them with the standard 1ctl deploy subcommands:
# Check pod health1ctl deploy status --deployment-id dep_01jbx9p3q4
# Stream logs1ctl logs --deployment-id dep_01jbx9p3q4
# Restart pods (e.g. after changing secrets)1ctl deploy restart --deployment-id dep_01jbx9p3q4
# Destroy the deployment and its volume1ctl deploy destroy --deployment-id dep_01jbx9p3q4Planned long-term CLI shape
Section titled “Planned long-term CLI shape”The current CLI is sensible for discovery:
1ctl marketplace list1ctl marketplace get postgres-161ctl marketplace deploy postgres-16 --name my-dbA strong future addition would be a canonical deploy entrypoint that makes the shared lifecycle explicit:
# Planned direction, not implemented today1ctl deploy --template postgres-16 --name my-dbIf added, 1ctl marketplace deploy can remain as an ergonomic alias for users who think “install Redis” before they think “create a deployment.” The important part is that both commands should compile into the same workload/deployment spec and pass through the same backend pipeline.
Capabilities that should stay shared with normal deploys
Section titled “Capabilities that should stay shared with normal deploys”| Capability | Marketplace-specific today? | Desired long-term rule |
|---|---|---|
| CPU and memory overrides | Partly duplicated | Reuse the standard deploy model. |
| Domains | Partly duplicated | Reuse the standard domains model. |
| Machine targeting | Marketplace has --hostname; deploy has --machine / --machine-tag | Converge on the deployment placement model. |
| Future machine selectors | Not available | Use the same future --machine-selector primitive. |
| Status, logs, restart, rollback, destroy | Already handled by deploy/logs | Keep fully shared. |
| Deployment strategies, HPA, VPA, PDB | Not exposed in marketplace | Prefer shared deployment capabilities unless a template explicitly forbids them. |
Current gaps to resolve before the model is complete
Section titled “Current gaps to resolve before the model is complete”| Gap | Why it matters |
|---|---|
| Separate marketplace deployment orchestration exists in the backend. | It can drift from ordinary deploy behavior over time. |
Marketplace deploy flags are not fully aligned with 1ctl deploy. | Users may learn two subtly different ways to express the same intent. |
Marketplace uses --hostname while deploy uses machine-oriented placement flags. | The BYOA placement model should become one language everywhere. |
| Rich templates can create more than a plain deployment, but the durable runtime contract is not yet documented as a shared workload spec. | Without a written contract, special cases can accrete into a second product. |
No canonical 1ctl deploy --template ... path exists yet. | The shared lifecycle is true conceptually, but not visible enough in the CLI. |
What happens under the hood
Section titled “What happens under the hood”Template resolution. marketplace_apps rows contain a full container spec: image, default CPU/memory, required env vars, port definitions, volume mount paths, and readiness probe configuration. When you deploy, the API reads this row and merges it with your request body — your supplied values replace the template defaults field-by-field.
Secret injection. Some templates (particularly databases) generate default credentials (usernames, passwords) automatically. These are provisioned via the Infisical integration and injected as Kubernetes Secrets, not ConfigMaps. Default credentials are displayed once in the deploy output; retrieve them later with 1ctl secret list.
Kubernetes resources created. Every marketplace deploy produces the same set of resources as 1ctl deploy:
| Resource | Purpose |
|---|---|
Deployment | Runs the container |
Service | ClusterIP or LoadBalancer endpoint |
HTTPRoute | HTTP(S) routing and TLS termination |
PersistentVolumeClaim | Durable storage (if --storage-size is set or the template requires it) |
Issuer | TLS certificate provisioning (if a custom domain is attached) |
All resources are created in your organization’s namespace and isolated from other organizations.
Backend notes for future consolidation
Section titled “Backend notes for future consolidation”Current implementation references in satusky-core_backend:
routes/marketplace_route.goexposes marketplace catalog endpoints and a dedicated deploy endpoint.controllers/marketplace_controller.gohandles marketplace deployment requests.services/marketplace_deployment_service.govalidates requests, creates deployment/service/ingress records, marksDeploymentSource: "marketplace", and dispatches app-specific orchestration.
The backend is already close to the desired conceptual model because marketplace deployments become ordinary deployment records. The remaining architectural work is to move from “separate marketplace pipeline that eventually creates deployments” toward “template resolution feeding the shared deployment pipeline.”