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
- Valid:
- 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
- Valid:
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) withwordpress-mariadb(database) - Valid:
moodle(main) withmoodle-mysql(database) - Invalid:
wordpress(main) withmariadb(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
portsdirective 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;memoryBaseis in gibibytes (GiB) - CPU: at least
0.5cores per container;cpuBaseis in decimal cores
- Memory: at least
- 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: secretin 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: containerReferencefor those variables - Container hostnames are resolved by name (e.g.,
postgres-dbresolves 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-metadatablock - Every service has
x-clouve-healthcheck(even ifenabled: 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.ymlpasses)
Exactly one public container
- Exactly one container has
isPublic: truefor 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: applicationUrlfor 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
- Containerization guide: how to create the
docker-compose.ymlandclv-docker-compose.ymlfiles - Container configuration: configuring containers in the UI or via YAML upload