Step 0
What you need
- A Tuploy account (Discord or Google OAuth at tuploy.com).
- A shell on Linux, macOS or Windows. No Node.js, no Docker, no Python — the CLI is a single Go binary.
- A project to deploy. The sample we use across all guides is a Next.js 15 + Prisma starter, but the same flags work for any supported framework.
Walkthrough
From install to live URL
Install the CLI
Linux & macOS:
curl -fsSL https://tuploy.com/install.sh | bashWindows (PowerShell):
iwr -useb https://tuploy.com/install.ps1 | iexThe installer downloads the binary from downloads.tuploy.com, verifies its SHA-256 checksum against tuploy.com/api/cli-releases (a separate host), then places it in ~/.local/bin/tuploy on Unix or %LOCALAPPDATA%\Tuploy\tuploy.exe on Windows.
Two-host integrity check
The binary travels overdownloads.tuploy.com and the expected checksum over tuploy.com. An attacker needs to compromise both hosts simultaneously to ship a tampered binary. If the hash mismatches, the installer aborts and deletes the partial download. Never run pipe-installers from sources you don't trust.Authenticate
The installer embeds a short-lived activation token and opens your browser at tuploy.com/activate. Approve in the browser; the CLI polls and stores the real API key in ~/.tuploy/config.json.
Skip the browser flow
If you already have an API key (from dashboard / API keys), just run:tuploy login tps_sk_your_key_heretuploy version & tuploy applications.$ tuploy version
tuploy 0.4.2
API: https://tuploy.com/apiDeploy your project
The most common case — no database, no extra flags. The CLI zips the directory (excluding node_modules, .git, .next, dist), uploads the ZIP and polls every 3 seconds until the deploy completes (max 6 minutes).
cd nextjs-app-sample
tuploy deploy .Apps that need a database get an extra dance — create the database first, then pass its URL via --env:
tuploy databases create app-db --type postgresql
# → returns id db_abc123 and a connection URL
tuploy deploy . \
--name nextjs-app \
--env DATABASE_URL=postgres://...On success the CLI writes a .tuploy.json next to your project so future redeploys reuse the same identity:
{
"projectSlug": "nextjs-app",
"appName": "nextjs-app",
"databaseId": "db_abc123",
"lastDeployId": "dep_xyz789"
}.tuploy.json to your .gitignore — it's a local link between your working copy and the Tuploy project, not source code.Redeploy
Same command, same directory. The CLI reads .tuploy.json, reuses the project, env vars and attached database, and ships a new version.
tuploy deploy .Reference
Deploy flags
| Flag | What it does |
|---|---|
| --name <name> | Project name. Becomes the subdomain ({name}.tuploy.app). Defaults to the directory name on the first deploy. |
| --env KEY=VALUE | Set an environment variable on the server. Repeatable. Overrides any .env in the ZIP. |
| --subdomain <sub> | Override the auto-generated subdomain. Requires account level 2+. |
| --domain <fqdn> | Attach a custom domain. Requires account level 2+ and a CNAME pointing at tuploy.com. |
Level-gated flags
--subdomain and --domain require account level 2 or higher (any past top-up qualifies). On level 1 the API returns 403 LEVEL_RESTRICTION.Reference
Database commands
Tuploy provisions managed databases on dedicated VMs. They're reachable only from your deployed app over the private network, not from the public internet.
List databases
tuploy databasesCreate a database
tuploy databases create <name> # postgresql (default)
tuploy databases create <name> --type mysql
tuploy databases create <name> --type mariadb
tuploy databases create <name> --type redis
tuploy databases create <name> --type mongodbEngine choices:
postgresql— Default. T3 stack, Prisma, SQLAlchemy, Django.mysql— PHP, Laravel, WordPress, classic LAMP.mariadb— MySQL-compatible drop-in, often used by CMS.redis— Cache, queues, session storage.mongodb— MERN stack, Mongoose, document-oriented apps.
Get connection details
tuploy databases get <id>$ tuploy databases get db_abc123
ID: db_abc123
Name: app-db
Type: postgresql
Status: running
Host: db-abc123.tuploy.internal
Port: 5432
URL: postgres://user:password@db-abc123.tuploy.internal:5432/app-dbThe returned URL is what you pass to --env DATABASE_URL=... on deploy. Never commit it.
Reference
Other commands
tuploy status <id>Print the status of a single deploy (queued, building, live or failed) plus the last log lines.tuploy applicationsList all your apps with their subdomain, tier and current status.tuploy billingOpen the billing page in your default browser.tuploy updateSelf-update the binary. Verifies the new binary's SHA-256 before the atomic replace.tuploy skill syncPropagate the Tuploy skill to other agents on the same machine (Cursor, Codex, Gemini, Kiro, Factory, Junie, Letta). Idempotent.CI/CD
Use the CLI in pipelines
For CI use a long-lived API key, not the interactive login. Create one from the API keys page and store it as a secret on your CI provider.
# .github/workflows/deploy.yml
name: Deploy to Tuploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Tuploy CLI
run: curl -fsSL https://tuploy.com/install.sh | bash
- name: Authenticate
run: tuploy login ${{ secrets.TUPLOY_API_KEY }}
- name: Deploy
run: tuploy deploy . --env DATABASE_URL=${{ secrets.DATABASE_URL }}Treat API keys like passwords
API keys grant the same access as your account: deploy, create databases, change env vars. Store them in your CI provider's secret store, never in committed files. Rotate periodically from the dashboard.Troubleshooting
Common errors
401 Unauthorized
Your API key is missing or expired. Run tuploy login <key> with a fresh one from the dashboard.
403 LEVEL_RESTRICTION
You're using --subdomain or --domain on an L1 account. Top up at least €0.01 to reach L2 and the gate lifts.
Deploy times out at 6 minutes
The build itself runs server-side without a 6-minute cap; only the CLI's polling does. If polling stops, run tuploy status <deploy-id> later to check the final state. The deploy id is in the CLI output and in .tuploy.json.
ZIP > 50 MB
The CLI excludes node_modules, .git, .next and dist automatically — most projects fit comfortably. If you still hit the limit, exclude large assets and host them on object storage instead.
Where to next

