Technical detail
Install Portainer-Run, point it at your Portainer Business instance and a Git repository, and your builders can deploy AI-generated apps with no Dockerfile, no registry, and no platform-engineering ticket. This page covers setup, the deployment flow, supported runtimes, and the technical detail behind it.
Before you start
Portainer-Run is a thin self-service layer on top of Portainer Business. It deploys nothing itself; it drives your Portainer instance, which drives your clusters.
Step 1
Deploy it into your cluster using the Kubernetes manifest provided in the GitHub repository at deploy/kubernetes.yaml. Set your Portainer URL and an encryption key, apply it, and reach it over HTTPS.
# Set PORTAINER_URL and ENCRYPTION_KEY (and optionally an AI key)
# in the manifest's env or a Secret, then apply it:
kubectl apply -f deploy/kubernetes.yaml
PORTAINER_URL and ENCRYPTION_KEY are the only required values. The encryption key protects stored Git credentials at rest and must be at least 32 characters (generate one with openssl rand -hex 32). Add ANTHROPIC_API_KEY or OPENAI_API_KEY to enable the Assistant. On first start it generates a self-signed certificate, so your browser warns once; accept it, or provide real certificates with SSL_CERT and SSL_KEY. Expose the service the way your cluster does, NodePort, LoadBalancer, or Ingress, and open it over HTTPS.
Portainer-Run also runs as a standalone container, and the Docker, Compose, and local development instructions are in the GitHub repository. That is only how you host Run itself. The apps it deploys always run on your Kubernetes clusters.
Step 2
Portainer-Run has no user database of its own. Access is your Portainer RBAC, carried by a personal access token.
In Portainer Business, go to Account, then Access Tokens, and create one. OAuth-only logins still generate a token here.
Navigate to https://your-host and enter the token. The environments and namespaces you can see and act on are exactly what your Portainer role grants. Admins additionally see Cluster Readiness and environment enable/disable controls.
Step 3
A Git target is an encrypted, stored connection to the repository Portainer-Run commits to. It is the system of record for every deployment.
Under Git Targets, add the provider (GitHub, GitLab, Gitea, or other), the repository in owner/repo form, a personal access token, a default branch, and an optional path prefix. Credentials are encrypted at rest with your encryption key. The Test button reports read and write access separately; a read-only token will fail at the commit step.
repo scope.Step 4
Drop the files an AI tool produced. No Dockerfile, no image, no CI. Portainer-Run detects the runtime, installs dependencies, commits to Git, and lets Portainer deploy it.
Upload the folder of files directly, or point Portainer-Run at an existing repository with a target, branch, and optional subfolder.
Pick the environment, namespace, and Git target from the options your token can reach, and the exposure type. If a .env.example is present, its keys appear as an editable environment-variable list, with secret-looking keys masked.
Portainer-Run commits and triggers the deployment. When it is live, the running status and access address (IP:port or FQDN) surface back to you.
Optional
Builders who work inside Claude or another MCP-capable assistant can deploy without opening Portainer-Run at all. Run exposes an MCP server, and the same governed Git and GitOps pipeline runs underneath, scoped by your Portainer RBAC.
Node.js must be installed on the developer's machine, since the connection runs through npx.
In Claude Desktop, go to File, then Settings, then Developer, then Edit Config.
Add the block below, pointing it at your Portainer-Run MCP endpoint and using a Portainer personal access token as the Bearer token. Your RBAC applies exactly as it does in the UI.
The Portainer-Run tools become available. Ask Claude to deploy an app and it runs through the same pipeline as the UI: committed to Git, deployed as a GitOps stack, governed by your rules.
"mcpServers": {
"portainer-run": {
"command": "npx",
"args": [
"-y",
"mcp-remote@latest",
"https://<your-portainer-run-host>/mcp",
"--header",
"Authorization: Bearer <your-portainer-access-token>"
],
"env": {
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
}
}
}
/mcp on your Portainer-Run host. The reference configuration exposes it on port 3001 (https://your-host:3001/mcp); confirm the port your deployment uses.npx is not found, set command to its full path, for example C:\\PROGRA~1\\nodejs\\npx.cmd.NODE_TLS_REJECT_UNAUTHORIZED: "0" is only needed while Portainer-Run uses its default self-signed certificate. Remove it once real certificates are in place.Under the hood
Everything is committed to Git and reconciled by Portainer, so each deployment is governed and fully repeatable.
Portainer-Run inspects the file structure and matches a runtime in priority order, first match wins. A package.json means Node.js, requirements.txt or .py means Python, .php means PHP, a Gemfile or .rb means Ruby. If everything is static assets it defaults to nginx.
Portainer-Run generates a Kubernetes Deployment manifest for the detected runtime, with three init containers that run in sequence before the app starts:
npm install --production, pip install -r requirements.txt, bundle install, or composer install) inside the runtime image so native modules build correctly..env file from the values entered at deploy time. These values are never committed to Git.Once the init containers complete, the main container starts against the pre-populated volume using a stock public runtime image. Git credentials are held in a Kubernetes Secret and injected by reference, so the token never appears in the pod spec.
Source files are committed to {environment}/{namespace}/{app}/src/ and the manifest to {environment}/{namespace}/{app}.yaml, keeping environments and namespaces cleanly separated in one repo. Portainer-Run then calls the Portainer API to create a GitOps stack pointed at that manifest. Portainer polls the repository on a set interval (five minutes by default) and applies any change.
An update takes the same path. Drop the revised files, Portainer-Run commits the change, and Portainer reconciles it on the next poll. The PersistentVolume keeps its state across restarts, so uploaded files and anything the app wrote to disk survive the update.
Reference
Detection is automatic and runs in priority order. If nothing matches, Portainer-Run falls back to nginx.
Start command from the start script, else node server.js / index.js / npm start.
Targets main.py, app.py, server.py, or run.py, else python app.py.
Served with Apache (php:8.3-apache).
Rack apps use bundle exec rackup, else app.rb or server.rb.
HTML, CSS, JS, images, and so on, on nginx:alpine. A single non-index.html file is renamed on commit.
Know before you go
Reference
| Variable | Required | Description |
|---|---|---|
PORTAINER_URL | Yes | Full URL of your Portainer instance, e.g. https://portainer.example.com:9443 |
ENCRYPTION_KEY | Yes | Encrypts stored Git credentials. At least 32 characters. Generate with openssl rand -hex 32. |
ANTHROPIC_API_KEY | No | Enables the Assistant and AI triage using Claude. |
OPENAI_API_KEY | No | Enables the Assistant using GPT-4o. Set one provider, not both. |
TEMPLATE_URL | No | URL of a custom application catalogue JSON file. |
SSL_CERT / SSL_KEY | No | Paths to real TLS certificate and key. Self-signed is used if unset. |
PORT / HTTP_PORT | No | Listen ports inside the container. Default 443 and 80. |
Go deeper
The repository has the complete reference: the deployment form, the application catalogue and template format, the Assistant, the aggregated status architecture, and every environment variable.