Distribute Documentation

Documentation

Everything you need to deploy apps, expose local servers, manage infrastructure, and ship PWAs · from your terminal.

Quick start v1.0

Install the CLI, log in once, and deploy from any project folder. The platform handles containerization, SSL, and routing automatically.

$curl -fsSL https://distribute.app/install.sh | sh $distribute login $distribute .
What's included

✓ 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.

Building on Distribute? Use the API.

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

$curl -fsSL https://distribute.app/install.sh | sh

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

# Download CLI directly $curl -fsSL https://distribute.app/cli/distribute -o distribute $chmod +x distribute && sudo mv distribute /usr/local/bin/

Requires: Python 3.8+, curl. The CLI is a single Python script with no external dependencies beyond the standard library and requests.

Authentication

$distribute login

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.

$distribute . Connected. 3 app(s) deployed. 1. ● my-api 2. ● storefront 3. + Deploy as new app Select [1-3]:

Deploy with a specific name (skips picker)

$distribute . --name my-awesome-api

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.

# .distributeignore node_modules .next dist public/videos .env.local *.log coverage

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.

ActionEffect
StartStarts the Docker container. built-in reverse proxy begins routing traffic to it.
StopStops the container. built-in reverse proxy stops routing · no traffic reaches the app.
RestartRestarts the container in place. Brief interruption (~1-2s).
DeleteRemoves 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.

$distribute http 3000 Tunnel open ──────────────────────────────────────────── Public URL → https://distribute.app/t/a3f82c1b Forwarding → localhost:3000 Press Ctrl+C to stop GET / → 200 POST /api/login → 201

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

FrameworkNotes
React CRAWorks. Static assets rewritten automatically.
Vite (React/Vue)Works. /assets/ paths rewritten.
AngularWorks. <base href> injected, absolute paths rewritten.
Next.jsWorks. /_next/ paths rewritten.
Vue CLIWorks. Static assets rewritten.
Any HTML serverWorks. 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)

# PostgreSQL $distribute tcp 5432 TCP Tunnel open Public URL → https://distribute.app/tcp/f1c90a22 Forwarding → localhost:5432 ↔ Connection c3a8b1f0 opened → localhost:5432

TLS (SSL-wrapped)

# HTTPS local server $distribute tls 8443 TLS Tunnel open Public URL → https://distribute.app/tcp/9d2e4b1a Forwarding → localhost:8443 (TLS wrapped)

Common use cases

CommandService
distribute tcp 5432PostgreSQL
distribute tcp 3306MySQL / MariaDB
distribute tcp 6379Redis
distribute tcp 27017MongoDB
distribute tcp 22SSH
distribute tls 8443Local HTTPS server
distribute tls 5671AMQPS (RabbitMQ)
distribute tls 8883MQTTS (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.

✓ Connected to distribute-my-api $python manage.py migrate Applying sessions.0001_initial... OK $cat .env | grep DATABASE DATABASE_URL=postgres://... $pip install psycopg2-binary Successfully installed psycopg2-binary-2.9.9

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

OutputDescription
manifest.jsonWeb App Manifest with name, icons, theme color, display mode
sw.jsService worker with offline-first cache strategy
Icons (6 sizes)192×192, 512×512, Apple touch icons, favicon
Android APKInstallable APK for sideloading on Android devices
iOS projectCapacitor 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:

# In your DNS provider myapp.com A → YOUR_SERVER_IP www.myapp.com CNAME → myapp.com

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

CommandDescription
distribute loginAuthenticate 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 /pathPackage 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 lsList 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:

{ "base_url": "https://distribute.app", "token": "your-auth-token", "username": "hamadi_camara" }

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

ValueBehaviour
manualOnly via dashboard button or distribute pipeline <app> trigger
mainAuto-deploy on push to main
masterAuto-deploy on push to master
anyAuto-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=1docker 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:

TypeUse case
AppExpose a deployed Docker app
FunctionExpose a serverless function
ExternalProxy 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

✗ SSLError: certificate verify failed # Fix: $pip3 install --upgrade certifi

Upload fails with SSL EOF

✗ SSLEOFError: EOF occurred in violation of protocol # Caused by large package or slow upload. The CLI uses curl # automatically. If curl is missing, install it: $brew install curl # macOS # Then reduce package size with .distributeignore

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

distribute: command not found # Fix: $export PATH="/usr/local/bin:$PATH" $source ~/.zshrc # or ~/.bashrc

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.