Documentation
Everything you need to deploy apps, expose local servers, manage infrastructure, and ship PWAs · from your terminal.
Install the CLI, log in once, and deploy from any project folder. The platform handles containerization, SSL, and routing automatically.
✓ Deploy any stack
✓ HTTP / TCP / TLS tunnels
✓ Browser terminal
✓ PWA builder
✓ Custom domains + SSL
✓ Start / stop / restart
✓ Admin panel
Introduction
Distribute is a self-hosted application deployment platform. You run it on your own server; your team uses the CLI or web dashboard to deploy, manage, and debug apps. It uses Docker for containerization and built-in reverse proxy for routing and SSL.
There are four main workflows:
Deploy · package a local folder and deploy it as a Docker container with a public URL and SSL certificate.
Tunnel · expose a local server (running on your laptop) to the internet via a secure public URL, without deploying it. Works for HTTP apps, databases, and any TCP/TLS service.
Terminal · open a browser-based shell inside any deployed container to run commands, debug, or inspect logs.
PWA Builder · analyze any deployed web app and generate a PWA package (manifest, service worker, icons) plus Android and iOS builds.
The CLI shown on this page is one client of the public REST API. If you're scripting deployments from CI, integrating with Claude Desktop / Cursor via MCP, or building your own SDK, see the
Developer API docs
for the /api/v1/* endpoints, scoped Bearer tokens, and the zero-dependency MCP server.
Installation
macOS / Linux
The installer detects your OS, downloads the latest CLI from your Distribute server, installs it to /usr/local/bin/distribute, and saves your server URL to ~/.distribute/config.json.
Manual install
Requires: Python 3.8+, curl. The CLI is a single Python script with no external dependencies beyond the standard library and requests.
Authentication
Running distribute login opens a browser window to your Distribute server. After signing in with Google, GitHub, or username/password, the tab closes automatically and the CLI stores a Bearer token in ~/.distribute/config.json.
The token is persisted on the server across restarts · you only need to log in once.
Token security: Your token is stored locally at ~/.distribute/config.json. Keep this file private. To revoke access, delete the token from your dashboard under Account Settings.
Deploying Apps
Interactive deploy
Run distribute . from your project folder. The CLI connects to the server, lists your existing apps, and lets you redeploy one or create a new deployment.
Deploy with a specific name (skips picker)
Supported stacks (auto-detected)
Docker Compose, Node.js, Python/Flask/Django, PHP, React, Vue, Angular, Next.js, Vite, Static HTML. If a docker-compose.yml or Dockerfile is present it takes precedence.
.distributeignore
Create a .distributeignore file in your project root to exclude files and directories from packaging. One pattern per line. Reduces upload size significantly.
The CLI also automatically skips: node_modules .git __pycache__ .next .nuxt venv android ios Pods and files over 50 MB.
App Lifecycle
Each deployed app can be started, stopped, restarted, or deleted from the dashboard. These actions control the underlying Docker container directly.
| Action | Effect |
|---|---|
| Start | Starts the Docker container. built-in reverse proxy begins routing traffic to it. |
| Stop | Stops the container. built-in reverse proxy stops routing · no traffic reaches the app. |
| Restart | Restarts the container in place. Brief interruption (~1-2s). |
| Delete | Removes the container and all app metadata. Cannot be undone. |
Domain uniqueness: No two apps can share the same custom domain. Attempting to configure a domain already used by another app returns a 409 error.
HTTP Tunnel
Expose a local HTTP server to the internet. Useful for sharing dev builds, testing webhooks, or demoing to clients without deploying.
How it works
The CLI opens a persistent long-polling connection to the server. Incoming HTTP requests to your public URL are forwarded to the CLI, which makes the request locally and returns the response. The server rewrites HTML responses to fix asset paths, fetch() calls, and redirects so that SPAs work correctly end-to-end.
Framework compatibility
| Framework | Notes |
|---|---|
| React CRA | Works. Static assets rewritten automatically. |
| Vite (React/Vue) | Works. /assets/ paths rewritten. |
| Angular | Works. <base href> injected, absolute paths rewritten. |
| Next.js | Works. /_next/ paths rewritten. |
| Vue CLI | Works. Static assets rewritten. |
| Any HTML server | Works. Plain HTTP proxied as-is. |
HMR / live reload: WebSocket-based hot module reload is neutralized (replaced with a no-op) because it cannot be proxied through HTTP polling. Pages load correctly · they just won't auto-reload on file changes.
Tunnels auto-expire after 1 hour. Ctrl+C closes the tunnel immediately.
TCP / TLS Tunnel
Expose any raw TCP or TLS service · databases, SSH, message brokers · via a public HTTP-based proxy. The tunnel bridges raw byte streams over HTTP polling.
TCP (plain)
TLS (SSL-wrapped)
Common use cases
| Command | Service |
|---|---|
| distribute tcp 5432 | PostgreSQL |
| distribute tcp 3306 | MySQL / MariaDB |
| distribute tcp 6379 | Redis |
| distribute tcp 27017 | MongoDB |
| distribute tcp 22 | SSH |
| distribute tls 8443 | Local HTTPS server |
| distribute tls 5671 | AMQPS (RabbitMQ) |
| distribute tls 8883 | MQTTS (MQTT broker) |
Browser Terminal
Every deployed app has a built-in terminal accessible from the dashboard. Click the >_ button on any app card to open a shell inside that container.
The terminal starts in the app's working directory (typically /app). It uses HTTP polling so it works reliably behind proxies, CDNs, and restrictive firewalls · no WebSocket required.
Press Ctrl+C to interrupt a running process. Close the modal to end the session.
PWA Builder
Navigate to Dashboard → PWA Builder and paste the URL of any web app. Distribute analyzes the site and generates a complete PWA package in seconds.
What gets generated
| Output | Description |
|---|---|
| manifest.json | Web App Manifest with name, icons, theme color, display mode |
| sw.js | Service worker with offline-first cache strategy |
| Icons (6 sizes) | 192×192, 512×512, Apple touch icons, favicon |
| Android APK | Installable APK for sideloading on Android devices |
| iOS project | Capacitor project ready for Xcode build and App Store submission |
The builder auto-detects the framework (React, Vue, Angular, Next.js, plain HTML) and adjusts the service worker caching strategy accordingly.
Custom Domains
Each app can have one or more custom domains. Add them from the dashboard by clicking Domains on any app card.
DNS setup
Point your domain to your server with an A record:
SSL is issued automatically via Let's Encrypt TLS-ALPN-01 challenge. No port 80 required. Certificate issuance typically completes within 2 minutes of DNS propagation.
Domain uniqueness: Each domain can only be assigned to one app. Attempting to use a domain already assigned elsewhere returns an error. Contact an admin to reassign.
CLI Reference
| Command | Description |
|---|---|
| distribute login | Authenticate via browser (device flow). Stores token locally. |
| distribute . | Package current directory, pick app interactively, deploy. |
| distribute . --name <n> | Deploy current directory with a specific app name (skips picker). |
| distribute /path | Package and deploy a specific directory. |
| distribute http <port> | Open an HTTP tunnel to localhost:<port>. |
| distribute tcp <port> | Open a TCP tunnel (databases, SSH, etc.). |
| distribute tls <port> | Open a TLS tunnel (SSL-wrapped services). |
| distribute ls | List your deployed apps with status. |
| distribute logs <app> | Tail container logs. |
| distribute delete <app> | Delete an app and its container. |
| distribute open <app> | Open the app URL in your browser. |
Config file
Stored at ~/.distribute/config.json:
Pipelines (CI/CD)
Pipelines auto-deploy your app on every git push. Connect your repository from the Pipelines panel or the CLI and choose a branch trigger.
Connect via dashboard
Go to Pipelines in the sidebar, click Connect a provider, authorize GitHub/GitLab/Bitbucket, then click Deploy or Link to app next to a repository.
A webhook is automatically created on the repository. No manual configuration needed.
Deploy from a repo (CLI)
distribute repo owner/myproject
distribute repo owner/myproject --name my-api
distribute repo owner/myproject --trigger main
Link an existing app to a repo (CLI)
distribute pipeline my-api link owner/myproject
distribute pipeline my-api link owner/myproject --trigger main
distribute pipeline my-api link owner/myproject --trigger any
Trigger options
| Value | Behaviour |
|---|---|
manual | Only via dashboard button or distribute pipeline <app> trigger |
main | Auto-deploy on push to main |
master | Auto-deploy on push to master |
any | Auto-deploy on push to any branch |
Pipeline commands (CLI)
distribute pipeline my-api # show status + recent builds
distribute pipeline my-api trigger # trigger manual build
distribute pipeline my-api link o/repo # link to repo
distribute pipeline my-api unlink # remove pipeline
Build flow
Each build: git clone --depth=1 → docker build (no cache) → image tagged with commit SHA → old container replaced → new container started with same built-in reverse proxy labels. No downtime for built-in reverse proxy routing.
Functions (Serverless)
Functions let you deploy lightweight event-driven code without managing containers. Each function runs in isolation and is invoked via HTTP.
Create a function
Go to Functions in the sidebar and click New function. Write your handler code in the editor. Functions support Python and Node.js.
Invoke a function
POST https://distribute.app/function/<name>
Content-Type: application/json
{"key": "value"}
CLI commands
distribute fn ls # list functions
distribute fn logs my-fn # view logs
distribute fn invoke my-fn # invoke (empty payload)
distribute fn invoke my-fn --data '{"user": "alice"}'
Expose a function as an API endpoint
Go to APIs and click New endpoint. Select type Function, pick your function, configure auth and the public URL. The endpoint will appear in the endpoints list.
API Gateway
The API Gateway lets you register, document and secure your deployed apps and functions as named API endpoints. Each endpoint can have rate limiting, API key auth and a custom URL.
Create an endpoint
Go to APIs and click New endpoint. Choose a type:
| Type | Use case |
|---|---|
| App | Expose a deployed Docker app |
| Function | Expose a serverless function |
| External | Proxy to an external URL or internal service |
Set a custom endpoint name, choose a domain (subdomain or custom domain) and optional path prefix. Auth can be set to Public or API key required.
API Keys
Generate named API keys from the APIs panel or via CLI. Keys are shown once on creation.
distribute api keys # list keys
distribute api key create mobile-app # generate new key
distribute api key delete <id> # delete key
Rate limiting
Click Configure on any endpoint to set requests-per-second and burst limits. Rate limiting is enforced by built-in reverse proxy and applied immediately.
CLI commands
distribute api ls # list your endpoints
Environment Variables
Manage environment variables for your apps from the CLI or dashboard (Applications → app card → Settings).
distribute env my-api set DATABASE_URL=postgres://... SECRET=abc
distribute env my-api ls
Variables are injected into the container at runtime. The app will restart automatically after an update.
Troubleshooting
SSL certificate error on upload
Upload fails with SSL EOF
Tunnel assets return 404 (Angular/React)
This happens when the app uses absolute paths like /styles.css which don't include the tunnel prefix. The CLI server rewrites HTML automatically, but if you see 404s on assets try adding a <base href="/"> tag explicitly in your index.html · the tunnel proxy will replace it with the correct prefix.
Command not found
Already logged in but CLI says "not logged in"
The server was restarted and a previous version lost the in-memory token. Since v1.0 tokens are persisted to disk · after a fresh deploy this should not occur. If it does, simply run distribute login again.