Application requirements

Prerequisites: Platform overview, Onboarding

This page lists what your application must satisfy before you can submit it to the Clouve marketplace. Work through it as a checklist before you start the submission process.


Overview

Clouve deploys your application to Kubernetes, so it must run well in a containerized, stateless, cloud-native environment. An app that works on a developer laptop but assumes persistent local storage, a fixed IP address, or a specific host OS will need adaptation.


Containerization requirements

Docker image

  • Every service in your application is packaged as a Docker image
  • Images are hosted in a publicly accessible registry, or in a registry the platform has credentials for
  • Images are tagged with a specific version, not latest
    • Valid: wordpress:6.7.1, postgres:16.4, myapp/backend:v2.3.0
    • Invalid: wordpress:latest, myapp/backend:main
  • Images are built for linux/amd64 (required) and optionally linux/arm64
  • Images do not require privileged mode (--privileged) to run

Image naming

  • Container names use lowercase letters, numbers, and hyphens only
  • Container names start and end with an alphanumeric character
  • Container names are 63 characters or fewer
  • No underscores or dots in container names
    • Valid: wordpress, api-backend, postgres-db
    • Invalid: WordPress, api_backend, postgres.db

Multi-container naming

If your application has multiple containers, prefix each supporting container (database, cache, and so on) with the main container's name:

  • Valid: wordpress (main) with wordpress-mariadb (database)
  • Valid: moodle (main) with moodle-mysql (database)
  • Invalid: wordpress (main) with mariadb (database, no prefix)

Service discovery and Kubernetes manifest generation both depend on this prefix.


Runtime requirements

Port exposure

  • Each container exposes exactly one port via the ports directive in Docker Compose
  • The port number is between 1 and 65535
  • The protocol is TCP (the default) or UDP; declare UDP explicitly if you use it

Stateless operation

  • The application starts fresh correctly if its container is restarted
  • Any state that must persist between restarts is stored in a named volume, not written to the container filesystem
  • The application does not assume a fixed hostname or IP address

Startup behavior

  • The application starts cleanly from a cold state, with no prior data and no existing configuration
  • First-run initialization (database schema creation, admin user setup) runs automatically on first start
  • Subsequent starts do not re-run initialization destructively
  • If the application depends on another container such as a database, it tolerates that container not being ready yet, either by retrying with backoff or through a health-check-aware startup sequence

Resource usage

  • The application operates correctly within the declared resource limits
  • Resource minimums enforced by the platform:
    • Memory: at least 1 (Gi) per container; memoryBase is in gibibytes (GiB)
    • CPU: at least 0.5 cores per container; cpuBase is in decimal cores
  • Recommended starting allocations (in GB / cores):
    • Frontend containers: 1 GB memory, 0.5 CPU
    • Backend containers: 1-2 GB memory, 0.5-1 CPU
    • Database containers: 2 GB memory, 1 CPU
    • Cache containers: 1 GB memory, 0.5 CPU

Configuration requirements

Environment variables

  • All runtime configuration is provided via environment variables, with no hardcoded values
  • Sensitive values (passwords, API keys, tokens) are not baked into the image
  • Optional configuration has default values
  • Required variables are clearly identified

See Environment variables for the full type system.

Secrets

  • Database passwords use type: secret in the manifest
  • API keys and tokens use type: secret
  • Secrets are never logged to stdout/stderr

Inter-container communication

  • Containers that reference other containers by hostname use type: containerReference for those variables
  • Container hostnames are resolved by name (e.g., postgres-db resolves to the Postgres container's ClusterIP)
  • No hardcoded IP addresses for inter-container communication

Health check requirements

  • At minimum, the main (public) container has a health check configured
  • HTTP health checks point to a real endpoint that returns 2xx when the app is healthy
  • The initial delay accounts for your application's startup time (30 to 60 seconds is common)
  • Health checks do not require authentication

See Health checks for configuration details.


Storage requirements

  • Any data that must survive a container restart is declared as a named volume
  • Volume sizes are specified with units: 8Gi, 10Gi, 100Gi
  • The minimum volume size is 8Gi (8 gibibytes); the manifest validator rejects anything smaller
  • Mount paths follow Linux conventions: /data, /var/lib/postgresql/data
  • The application writes to the declared mount path, not to the container's root filesystem

See Volumes and storage for configuration details.


Manifest requirements

clv-docker-compose.yml

  • Every service in the manifest has an x-clouve-metadata block
  • Every service has x-clouve-healthcheck (even if enabled: false)
  • Every service has x-clouve-environment-types (even if empty: {})
  • Every volume declared under a service has a corresponding entry in x-clouve-volumes
  • The file parses without errors (yamllint clv-docker-compose.yml passes)

Exactly one public container

  • Exactly one container has isPublic: true for single-app submissions
  • For bundles, each app in the bundle has exactly one public-facing container

Application URL handling

  • If your application needs to know its own public URL, for example to generate links or set base URLs, use type: applicationUrl for that variable
  • Do not hardcode a domain name in the manifest

Security requirements

  • The Docker image does not run as root if avoidable
  • No hardcoded credentials in the image layers or manifest
  • The application does not open unexpected ports beyond what is declared
  • Database containers are marked isPublic: false

Testing checklist

Before submitting, run through this validation sequence:

# 1. Validate your YAML syntax
yamllint clv-docker-compose.yml

# 2. Start fresh and verify initialization
docker-compose down -v
docker-compose up

# 3. Verify the app is reachable (replace 80 with your port)
curl http://localhost:80/

# 4. Test a restart (simulates container crash/restart)
docker-compose restart <main-container>

# 5. Verify data persists across restarts
# (create some data in the app, restart, verify it's still there)
docker-compose restart <database-container>

Next steps