Guides

Guides

Step-by-step walkthroughs for deploying every type of app, connecting integrations, and managing your deployments.

Quick Start

Go from zero to a live app in three steps.

1

Install the CLI

Terminal

$ curl -fsSL https://leash.build/install.sh | sh

2

Sign in

Terminal

$ leash login

? How would you like to sign in?

> Continue with Google

Email & Password

3

Deploy

Terminal

$ cd my-app && leash deploy

...

✓ Deployed successfully!

→ https://my-app-arvin.un.leash.build

That's it. Your app is live with HTTPS, a custom subdomain, and zero configuration.

Deploy HTML

Deploy a single HTML file or a directory containing an index.html. Perfect for Claude Code artifacts, prototypes, and quick demos.

Single file

Use the --html flag to deploy any standalone HTML file.

Terminal

$ leash deploy --html artifact.html

✓ Deployed static site

→ https://artifact-arvin.un.leash.build

Directory with index.html

If your directory contains an index.html, Leash auto-detects it as a static site.

Terminal

$ cd my-static-site

$ leash deploy

✓ Detected static site

✓ Deployed successfully!

→ https://my-static-site-arvin.un.leash.build

Tip: Claude Code artifacts

When Claude generates an HTML artifact, save it to a file and deploy it in one command. Great for sharing interactive visualizations, dashboards, and prototypes with teammates.

Deploy Next.js

Leash detects Next.js automatically from your package.json. Before deploying, it runs a preflight build locally to catch errors early.

Terminal

$ cd my-nextjs-app

$ leash deploy

✓ Detected Next.js 15

✓ Preflight build passed

✓ Image built and pushed

✓ Deployed successfully!

→ https://my-nextjs-app-arvin.un.leash.build

Common issue: TypeScript target

If your build fails with syntax errors, your tsconfig.json may be targeting ES5. Update it to a modern version:

{
"compilerOptions": {
"target": "ES2017",
"module": "esnext"
}
}

What the preflight checks

  • Runs npm run build locally
  • Catches type errors before remote build
  • Validates that the build output is correct

Deploy Flask

Leash detects Python apps by the presence of requirements.txt. Flask apps are auto-detected and served with gunicorn.

Example app

app.py
from flask import Flask, jsonify
app = Flask(__name__)
@app.route("/")
def index():
return "<h1>Hello from Leash!</h1>"
@app.route("/api/health")
def health():
return jsonify({"status": "ok"})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080)

Dependencies

requirements.txt
flask
gunicorn

Deploy

Terminal

$ leash deploy

✓ Detected Flask (Python)

✓ Image built and pushed

✓ Deployed successfully!

→ https://my-flask-app-arvin.un.leash.build

Tip

Include gunicorn in your requirements.txt. Leash uses it as the production server automatically.

Deploy Express

Any Node.js app with a package.json containing a start script deploys out of the box.

Example app

index.js
const express = require('express')
const app = express()
const port = process.env.PORT || 8080
app.use(express.json())
app.get('/', (req, res) => {
res.json({ message: 'Hello from Leash!' })
})
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', uptime: process.uptime() })
})
app.listen(port, () => {
console.log(`Server running on port ${port}`)
})

package.json

package.json
{
"name": "my-express-app",
"version": "1.0.0",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.18.0"
}
}

Deploy

Terminal

$ cd my-express-app

$ leash deploy

✓ Detected Node.js

✓ Image built and pushed

✓ Deployed successfully!

→ https://my-express-app-arvin.un.leash.build

Important: use process.env.PORT

Leash injects the PORT environment variable at runtime. Make sure your app listens on process.env.PORT instead of hardcoding a port number.

Deploy Go

Leash detects Go apps by the presence of go.mod. Just deploy from your project directory.

Example app

main.go
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from Leash!")
})
http.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
})
fmt.Printf("Server running on port %s\n", port)
http.ListenAndServe(":"+port, nil)
}

go.mod

go.mod
module my-go-service
go 1.22

Deploy

Terminal

$ cd my-go-service

$ leash deploy

✓ Detected Go

✓ Image built and pushed

✓ Deployed successfully!

→ https://my-go-service-arvin.un.leash.build

Deploy Docker

If a Dockerfile exists in your project root, Leash uses it directly instead of auto-detecting a framework. This gives you full control over the build process.

Terminal

$ leash deploy

✓ Detected Dockerfile

✓ Building from Dockerfile...

✓ Image built and pushed

✓ Deployed successfully!

→ https://my-app-arvin.un.leash.build

When to use a Dockerfile

  • You need system-level dependencies not covered by Nixpacks
  • You want multi-stage builds for smaller images
  • Your app uses a language or framework Leash doesn't auto-detect
  • You need precise control over the runtime environment

Important: expose the right port

Your container must listen on the port specified by the PORT environment variable (defaults to 8080). Use EXPOSE 8080 in your Dockerfile and read $PORT at runtime.

Integrations

Leash integrations let your deployed apps access third-party services on behalf of each user. Connect once on the dashboard, and every app you deploy can use them.

How it works

Connect once, use everywhere

Connect your accounts on leash.build. Every app you deploy can access them without per-app configuration.

Per-user auth model

When someone uses your app, they authorize with their own account. Your app never sees other users' data.

Two access modes

Access integrations via the TypeScript SDK (REST) or through the MCP protocol for AI agents.

Available providers

Gmail

Send, read, and search email

Calendar

List events, create meetings

Drive

List, read, and search files

Connect flow

1

Go to your Dashboard on leash.build and navigate to Integrations

2

Click Connect next to the provider you want and authorize access via OAuth

3

Done. All your deployed apps can now use this integration.

Or connect from the CLI:

Terminal

$ leash integrations:connect gmail

✓ Gmail connected for @arvin

Terminal

$ leash integrations:status

gmail connected

calendar not connected

drive not connected

Databases

Leash provides managed PostgreSQL databases. When you deploy an app that uses a database, the CLI detects it and offers to create one for you.

Automatic Detection

The CLI detects database dependencies in your package.json, requirements.txt, go.mod, etc. If it finds PostgreSQL packages (pg, prisma, psycopg, pgx, etc.), it offers to provision a database.

Terminal

$ leash deploy

✓ Detected: flask (python)

⚠ Your app uses PostgreSQL (psycopg)

Options:

[1] Create a Leash database (free with your plan)

[2] Paste your own database URL

[3] Skip — I'll configure it later

Choice (1/2/3): 1

✓ Database created — DATABASE_URL injected

How It Works

  • Each app gets its own isolated PostgreSQL database and user
  • DATABASE_URL is automatically injected as an environment variable
  • Your ORM (Prisma, SQLAlchemy, etc.) creates tables on first connection
  • Daily automated backups with 7-day retention

Limits

Max connections per app10
Query timeout30 seconds
Storage (Free)50 MB
Storage (Individual)500 MB
Storage (Pro)2 GB
Storage (Team)10 GB

Common Issues

  • "too many connections" — Use connection pooling (PgBouncer or app-level). Close connections after each request. Max 10 concurrent per app.
  • "canceling statement due to statement timeout" — Query exceeded 30 seconds. Optimize your query, add indexes, or paginate results.
  • "connection refused" — DATABASE_URL only works from apps deployed on Leash. It uses an internal socket connection.

Bring Your Own Database

If you prefer to use your own database (Supabase, Neon, MongoDB Atlas, etc.), choose option [2] during deploy or set the URL manually:

Terminal

$ leash env set DATABASE_URL postgresql://user:pass@host:5432/dbname

For MongoDB, use MONGODB_URI. For Redis, use REDIS_URL.

Environment Variables

Set secrets and configuration for your deployed apps. Variables are encrypted at rest and injected at runtime. You must redeploy for changes to take effect.

Set a variable via CLI

Terminal

$ leash env set DATABASE_URL "postgresql://user:pass@host/db"

✓ DATABASE_URL set

? Redeploy now to apply changes? (Y/n)

The CLI prompts you to redeploy immediately so the new value takes effect.

List variables

Terminal

$ leash env list

DATABASE_URL = postgresql://...

API_KEY = sk-****

NODE_ENV = production

Delete a variable

Terminal

$ leash env delete API_KEY

✓ API_KEY deleted

Dashboard management

You can also manage environment variables from the dashboard. Navigate to your app, click Settings, and scroll to the Environment Variables section. The dashboard provides a Save & Redeploy button that applies changes and triggers a fresh deploy in one step.

Remember: redeploy to apply

Environment variables are injected at container start time. Changing a variable does not affect a running deployment -- you must redeploy for the new value to take effect.

Access Control

Control who can access your deployed apps. Set visibility during deploy or change it later from the dashboard.

Public

Anyone with the URL can access the app. This is the default for all deployments.

Private

Only the app owner can access it. Requires login to view.

Team

Only members of your organization can access. Requires Google sign-in with a matching company email domain.

Set visibility during deploy

Terminal

$ leash deploy --visibility private

# Options: public, private, team

Change visibility later

Open your app in the Dashboard, go to Settings, and update the visibility level. Changes take effect immediately -- no redeploy required.