deploy_app
ActiveTool of cloud.redu/mcp
Deploys an app to a VM and exposes it at a public https://<name>-<id>.redu.cloud URL. The container is built ON the VM. PREREQS — run check_deploy_prerequisites first for network_id + keypair_name, then plan_deploy for cost approval. Source can be git repo or prepare_upload source_token. PORT must be the real app listen port. To wire a DB, pass database:'managed' (dedicated managed datastore VM on the same private network, reused on same-name redeploy) or database:'single_vm' for Postgres on the app VM. Choose db_engine ('postgres' default; 'mysql'/'mariadb' for WordPress/Matomo/LAMP, managed only). For WordPress/WooCommerce cluster intent, do not use generic stateless deploy: pass app_profile, cluster_target:true, database:'managed', db_engine:'mariadb' or 'mysql', cluster_media_mode:'media_space', and either media_space_id or create_media_space:true. Redu mounts the media space into wp-content/uploads and refuses unsafe local uploads. Build+provision takes minutes; poll list_deployments/get_deployment.
Parameters schema
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"name",
"keypair_name"
],
"properties": {
"env": {
"type": "object",
"description": "Env vars injected into the container at deploy time (e.g. PGHOST/PGPORT/PGUSER/PGPASSWORD/PGDATABASE from a managed Postgres). Never baked into the image.",
"additionalProperties": {
"type": "string"
}
},
"name": {
"type": "string",
"maxLength": 63,
"minLength": 1,
"description": "Name for the deployment / VM (lowercase letters, numbers, hyphens)."
},
"port": {
"type": "integer",
"default": 3000,
"maximum": 65535,
"minimum": 1,
"description": "REQUIRED in practice: the port the app actually listens on inside the container — pass the port plan_deploy detected / the Dockerfile EXPOSE / the framework default. redu probes THIS port for health, so a wrong value fails the deploy. Defaults to 3000 only if omitted (e.g. a static nginx app listens on 80 — pass 80). IGNORED when worker:true."
},
"repo": {
"type": "string",
"format": "uri",
"description": "GIT MODE: public git repo URL (https). For a PRIVATE repo also pass git_token. Omit when using source_token (upload mode)."
},
"db_id": {
"type": "integer",
"description": "Informational link to a managed Postgres (from create_database/list_databases).",
"exclusiveMinimum": 0
},
"dname": {
"type": "string",
"description": "Custom *.redu.cloud subdomain. For a STABLE, KNOWN-AHEAD URL (needed when the app must be told its OWN url — OAuth callbacks, cookie domain, a frontend that calls its API), generate the FULL auto-gen form yourself: `<label>-<8 lowercase letters/digits>.redu.cloud` (e.g. `myapp-7k2m9x4p.redu.cloud`) — that exact form is used VERBATIM — and wire the app's own URL env to that SAME value. A BARE `<label>.redu.cloud` is NOT used as-is: redu appends a random 8-char suffix for uniqueness, so anything wired to the bare name will NOT match the real URL. Omit to auto-generate (then read the real URL from get_deployment)."
},
"redis": {
"enum": [
"none",
"managed"
],
"type": "string",
"description": "Redis wiring (auto-injects REDIS_URL/REDIS_HOST/REDIS_PORT/REDIS_PASSWORD — zero setup): 'managed' = a SEPARATE managed Redis VM auto-provisioned + wired on the same private network (data persists across app redeploys; reused on a same-name redeploy). Omit (or 'none') for no Redis. With 'managed' you do NOT call create_redis — it's done for you. Use when the app needs Redis (REDIS_URL / bullmq / celery / sidekiq / cache)."
},
"subdir": {
"type": "string",
"description": "Build context within the source (e.g. 'demo-go') when the Containerfile/Dockerfile isn't at the root."
},
"worker": {
"type": "boolean",
"description": "HEADLESS WORKER / daemon mode. Deploys a long-running container with NO HTTP server, NO public URL, and NO health probe — ready = the container stays running. The VM still gets a floating IP + SSH stream (so you can SSH in), but nothing is exposed on the web. Use for background workers, a chaos-monkey, an on-infra agent runner, cron/queue consumers. `port`, `dname`, and the injected APP_URL/PUBLIC_URL are ignored. Run plan_deploy with worker:true too."
},
"context": {
"type": "string",
"description": "Build-context dir within the source (default: repo root, or `subdir`). Set when the Dockerfile lives in a subfolder but builds from the repo root."
},
"db_name": {
"type": "string",
"description": "DB name for single_vm/managed (default 'app')."
},
"db_user": {
"type": "string",
"description": "DB user for single_vm/managed (default 'appuser')."
},
"git_ref": {
"type": "string",
"description": "git mode only: branch/tag/commit to deploy (default: the repo's default branch)."
},
"runtime": {
"type": "string",
"description": "Informational: node/python/go/… (e.g. from plan_deploy)."
},
"database": {
"enum": [
"none",
"single_vm",
"managed"
],
"type": "string",
"description": "DB wiring (auto-injects the connection env + DATABASE_URL — zero setup): 'single_vm' = Postgres ON the app VM (cheapest, data dies when the VM is replaced; Postgres only); 'managed' = a SEPARATE managed-DB VM auto-provisioned + wired on the same private network (data PERSISTS across app redeploys; reused on a same-name redeploy). Omit (or 'none') for no DB. With 'managed' you do NOT call create_database/create_relational_database — it's done for you. Choose the engine with db_engine."
},
"db_engine": {
"enum": [
"postgres",
"mysql",
"mariadb"
],
"type": "string",
"description": "Managed-DB engine (default 'postgres'). 'mysql'/'mariadb' provision a managed MySQL/MariaDB VM and wire MYSQL_HOST/MYSQL_PORT/MYSQL_USER/MYSQL_PASSWORD/MYSQL_DATABASE + a mysql:// DATABASE_URL — use for WordPress/Matomo/NextCloud/most PHP-LAMP apps. Requires database:'managed' (single_vm is Postgres only)."
},
"flavor_id": {
"type": "string",
"default": "3",
"minLength": 1,
"description": "Instance size — from list_flavors. Default m1.medium (enough RAM to build on the VM)."
},
"git_token": {
"type": "string",
"description": "git mode only: token to clone a PRIVATE repo. Omit for public repos."
},
"db_version": {
"type": "string",
"description": "DB version for single_vm/managed. Postgres: '16'|'15'|'14' (default 16). MySQL: '8.0'. MariaDB: '11.4'|'10.11'. Defaults per engine if omitted."
},
"dockerfile": {
"type": "string",
"description": "Path within the source to the Dockerfile (e.g. 'scripts/Dockerfile') when it's NOT at the root. Pair with `context` when the Dockerfile is in a subfolder but COPYs from the repo root."
},
"network_id": {
"type": "string",
"minLength": 1,
"description": "Existing private network id — from check_deploy_prerequisites (auto-selects your default) or list_private_networks. Optional: if omitted, redu auto-selects your default network."
},
"app_profile": {
"enum": [
"generic",
"wordpress",
"woocommerce"
],
"type": "string",
"description": "Detected app profile from source inspection. For WordPress/WooCommerce pass wordpress/woocommerce so cluster deploys enforce managed DB + shared media instead of treating local uploads as stateless."
},
"db_flavor_id": {
"type": "string",
"description": "managed only: VM size for the dedicated Postgres (from list_flavors). Defaults to the app flavor; m1.small is plenty for most. plan_deploy sizes this for you."
},
"db_superuser": {
"type": "boolean",
"description": "managed/single_vm Postgres only: grant the app DB user SUPERUSER (dedicated single-tenant DB VM, so safe). Use when the app's migrations create extensions/roles themselves."
},
"keypair_name": {
"type": "string",
"minLength": 1,
"description": "REQUIRED. An EXISTING SSH keypair name — call list_keypairs and reuse one, or import_keypair first."
},
"source_token": {
"type": "string",
"minLength": 16,
"description": "UPLOAD MODE: token from prepare_upload's curl step — deploys an uploaded tarball of your LOCAL working dir (no git, no PAT). Use this to deploy uncommitted code, a fixed clone of a repo you don't own, or private code. Omit `repo` when set."
},
"db_extensions": {
"type": "array",
"items": {
"enum": [
"vector",
"pgvector",
"postgis",
"pgaudit",
"pg_stat_statements",
"hstore",
"pg_trgm",
"uuid-ossp",
"citext",
"pgcrypto",
"ltree",
"btree_gin",
"btree_gist"
],
"type": "string"
},
"description": "managed/single_vm Postgres only: extensions to pre-install (pgvector, postgis, pgaudit, pg_trgm, …). Pass when the app needs one — e.g. langfuse/lantern need pgvector — so you don't have to CREATE EXTENSION by hand."
},
"redis_version": {
"enum": [
"7",
"6"
],
"type": "string",
"description": "managed Redis only: version (default '7')."
},
"cluster_target": {
"type": "boolean",
"description": "Set TRUE when the user asked to deploy this app as an autoscaling cluster. For WordPress/WooCommerce this makes the backend require managed MySQL/MariaDB plus media_space/central_media_origin before deployment can proceed."
},
"media_space_id": {
"type": "integer",
"description": "Existing Redu media space id to mount at WordPress wp-content/uploads. Get it from list_media_spaces.",
"exclusiveMinimum": 0
},
"redis_password": {
"type": "string",
"description": "managed Redis only: a specific password to set (otherwise auto-generated)."
},
"idempotency_key": {
"type": "string",
"minLength": 8
},
"redis_flavor_id": {
"type": "string",
"description": "managed Redis only: the dedicated Redis VM size (from list_flavors). Defaults to the app flavor; m1.small is plenty for a cache."
},
"media_mount_path": {
"type": "string",
"description": "Host mount path on the app VM/member. Redu mounts this into /var/www/html/wp-content/uploads."
},
"media_origin_url": {
"type": "string",
"format": "uri",
"description": "Public base URL where WordPress wp-content/uploads is served when using central_media_origin. Do not put this on the DB VM."
},
"media_space_name": {
"type": "string",
"maxLength": 63,
"minLength": 1,
"description": "Optional media space name when create_media_space:true. Defaults to <deployment-name>-media."
},
"cluster_media_mode": {
"enum": [
"local_uploads",
"central_media_origin",
"media_space"
],
"type": "string",
"description": "Record the WordPress media strategy for future clustering. For real WordPress clusters use media_space by default: Redu mounts a shared uploads filesystem into every member. local_uploads is single-VM only and upgrade_to_cluster will refuse it for WordPress."
},
"create_media_space": {
"type": "boolean",
"description": "For WordPress/WooCommerce cluster_target:true: set TRUE when no suitable media space exists. Redu creates an NFS media VM + persistent volume and mounts it into /var/www/html/wp-content/uploads."
},
"dockerfile_content": {
"type": "string",
"description": "A Dockerfile to write into the build dir before building — pass the Dockerfile that plan_deploy generated when the repo has none, or to override a broken one. (In upload mode you can instead just include the Dockerfile in the tarball.)"
},
"media_space_size_gb": {
"type": "integer",
"maximum": 2048,
"minimum": 1,
"description": "Media space data volume size in GB when create_media_space:true (default 20)."
},
"security_group_names": {
"type": "array",
"items": {
"type": "string"
},
"default": [
"default"
]
},
"media_space_flavor_id": {
"type": "string",
"minLength": 1,
"description": "Flavor id for the media VM when create_media_space:true. Defaults to m1.small/backend default."
}
},
"additionalProperties": false
}Parent server
cloud.redu/mcp
1/7 registries