Skip to content

Project profiles

A project is rarely one port. Laravel wants an app server and Mailpit and a database; WordPress sits on a whole LAMP stack. Project profiles describe the stack once (committed to the repo) so bare xport exposes everything in one command:

Terminal window
cd my-laravel-app
xport
____ _
__ _| _ \ ___ _ __| |_
\ \/ / |_) / _ \| '__| __|
> <| __/ (_) | | | |_
/_/\_\_| \___/|_| \__|
Project: my-laravel-app (laravel)
LABEL URL LOCAL
Web https://k7fz2.xport.sh localhost:8000
Mailpit https://q2mw9.xport.sh localhost:8025
private: db — reachable (local only), never exposed
Group: my-laravel-app — stop all with: xport stop --group my-laravel-app
Expires: 2026-11-03 18:30:00 CET
Press Ctrl-C to stop all.
  1. xport.yaml in the project root — explicit profile, always wins.
  2. Markers — a known file selects a built-in template: artisan → laravel, wp-config.php → wordpress, package.json → node.
  3. Neither → xport behaves as always (stdin becomes a paste, a terminal shows help).
Terminal window
xport profile init laravel # built-in template
xport profile init # generic scaffold
xport.yaml
profile: laravel # informational: which template it came from
name: my-laravel-app # groups the shares in the registry
services:
app:
port: 8000
label: Web
check: php artisan migrate:status # preflight command (optional)
hint: php artisan key:generate # shown when the check fails
mail:
port: 8025
label: Mailpit
optional: true # skipped with a warning instead of aborting
private:
db: 3306 # verified locally, NEVER exposed

Commit the file: teammates clone, run xport, and get the identical setup — no README section to follow.

Before any tunnel starts, every service is verified:

  • Port check — something must listen on the port. A closed required port aborts with the service’s hint (a dead URL helps nobody); an optional service is skipped with a warning.
  • check commands — run after the port check; failure aborts (or skips, when optional) with the captured error and the hint.

A profile’s check commands are arbitrary shell — a cloned repository could smuggle anything into them. So an xport.yaml file carrying check commands asks before running them, once per file (its hash is remembered); --trust opts out. Built-in templates and marker detection ship with the CLI and are trusted by construction. In --json or detached runs, where no one can answer, an untrusted profile aborts with a clear error instead of guessing.

  • xport list shows every service with its group; xport stop --group <name> stops the whole project at once.
  • --json emits one envelope with all services, URLs, and ports — nice for scripts and CI.
  • All persistent flags apply (--expires, --detach, …).
Template Services Private
laravel Web (8000, checks php artisan --version), Mailpit (8025, optional) db (3306)
wordpress Web (8080) db (3306)
node Web (3000) —

Ports are starting points — edit xport.yaml to match your stack (Valet, LocalWP, Sail, custom ports). More templates ship as the ecosystem grows.