CLI Reference

CLI Commands

Full reference of commands and flags.

The ngris binary has two command families: tunneling commands (ngris http, ngris tcp, ngris webfm…) that run a live agent from your machine, and application-hosting commands (ngris apps list, ngris deploy, ngris apps promote…) that build and ship apps to the Ngris edge. They're the same binary; see Application hosting commands below for the deploy family.

Global

Top-level commands and help.

Endpoints

Forward HTTP, TCP or UDP traffic, or expose a directory as a web file manager. On a TCP tunnel, add --proxy-protocol v1|v2 to hand the backend the real client IP.

Traffic policy

Attach edge rules (IP allow/deny, rate limit, header rewrites, force-HTTPS, compression) at connect time — from shorthand flags or a --traffic-policy file. Requires the traffic-policy entitlement. See Traffic Policy Rules → Configure from the agent CLI for the file format.

Endpoint auth

Put an endpoint behind a login at connect time: HTTP Basic auth (--basic-auth user:pass, repeatable, passwords ≥ 8 chars; turns on username/password sign-in only) and/or mutual TLS (--mutual-tls disabled|optional|required with --client-ca <file>; mTLS needs the mtls_attachment entitlement; client certificates are checked on HTTP(S) endpoints only, so use them with ngris http). Restarting without a flag removes what it had set (a run that logs in with a key doesn't: see Key authentication below); settings made in the dashboard stay. See Security & Access → Endpoint authentication from the agent CLI.

Key authentication (Preview)

Log a tunnel in with a key instead of a token (see Key authentication). ngris key sign <code> signs the code shown in the dashboard (Security → Agent keys → Upload key); paste its output there. It signs with --private-key-file <file> if given, else the key saved with ngris auth, else ~/.config/ngris/agent_key. ngris auth --private-key-file <file> saves the key for every tunnel; --auth-method token|key and --private-key-file <file> set it for one tunnel command. Environment variables can't set them. A key covers tunnels only: app commands and the endpoint auth and traffic policy flags (and webfm --rw) need a token.

Client certificates

Get a certificate for a device from a certificate authority ngris manages for your account (create the authority in the dashboard: Security → mTLS). ngris client-cert request makes the key on the device and writes <device>.crt, <device>.key and ca.pem. Files are never overwritten, so renew into a new folder with --out-dir. list shows your certificates and revoke makes your endpoints refuse one from the device’s next request. See Security & Access → Let ngris issue the certificates.

HTTPS backend

Give the http command an https:// local address and the agent forwards over TLS, verifying the backend certificate — --upstream-ca <file> for a private CA, --upstream-client-cert/--upstream-client-key for mTLS, --upstream-server-name for the SNI, and --upstream-host to rewrite the Host header the backend sees. The local port is optional — it defaults from the scheme (https://→:443, http://→:80). See Connect to an HTTPS backend below.

Inspector

Tail, search, replay, and export the requests captured by a running tunnel, over its local loopback API. replay re-sends a captured request to the tunnel's own local backend.

Config & Diagnostics

Run bare ngris for a quick HTTP tunnel to localhost:80 — it accepts the same http flags and loads your personal config.yaml (or the file you pass with --config; older versions also read config.yaml from the working directory); override settings with the agent’s environment variables (for example API_TOKEN, AGENT_REGION). ngris config check validates the resolved config (file + env) without connecting — a non-zero exit means it's invalid.

Global
ngris --version
ngris --help
ngris auth --token xyz
ngris auth --private-key-file ~/.config/ngris/agent_key   # key authentication (Preview)
ngris key sign <code>
ngris version
Endpoints
ngris http <addr> [flags]
ngris tcp <addr> [flags]
ngris udp <addr> [flags]
ngris webfm <path> [--rw]       # Web File Manager (read-only by default)
ngris tcp 5432 --proxy-protocol v2  # preserve the real client IP (PROXY protocol; TCP only)
Traffic policy
ngris http 3000 --cidr-allow 10.0.0.0/8 --rate-limit 100/1m
ngris http 3000 --force-https --response-header-add X-Frame-Options:DENY
ngris http 3000 --traffic-policy ./policy.yaml   # YAML/JSON rule file
Endpoint auth
ngris http 3000 --basic-auth alice:s3cretpw          # HTTP Basic (repeat for more users)
ngris http 3000 --mutual-tls required --client-ca ./client-ca.pem
ngris webfm ./site --rw --basic-auth admin:s3cretpw  # protect a writable file server
Key authentication (Preview)
ngris key sign <code>                                     # paste the output into the dashboard
ngris key sign --private-key-file ./agent_key <code>      # a key in another place
ngris auth --private-key-file ~/.config/ngris/agent_key   # use the key for every tunnel
ngris http 3000 --auth-method key --private-key-file ./agent_key
Client certificates
ngris client-cert request --ca "Company laptops" --name laptop-anna   # key made on this device
ngris client-cert request --ca "Company laptops" --name laptop-anna --out-dir ./renewed   # renew
ngris client-cert list [--ca <name|id>]
ngris client-cert revoke <id> [--reason TEXT] [--yes]
HTTPS backend
ngris http https://localhost:8443                        # verified TLS to the backend
ngris http https://example.com --upstream-host rewrite   # public vhost/CDN (:443 default, sends Host: example.com)
ngris http https://localhost:8443 --upstream-ca ./ca.pem   # private / self-signed CA
ngris http https://localhost:8443 \
  --upstream-client-cert ./client.pem --upstream-client-key ./client.key   # mTLS
Inspector
ngris inspect --status 4xx,5xx [--search <str>] [--follow] [--json]
ngris replay --id <req_id> --set-header "X-Debug: 1"
ngris export --format json > requests.json
Config & diagnostics
# Bare ngris = HTTP tunnel to localhost:80 (accepts the http flags),
# reading your personal config.yaml (or --config <file>)
ngris
ngris --basic-auth admin:s3cretpw --url myapp.ngris.io   # bare + http flags

# Validate config (file + env) without connecting — non-zero exit if invalid
ngris config check
ngris config check --config ./config.yaml --json

# Region preference via environment variable (the server address
# itself is not configurable: discovery picks it)
AGENT_REGION=us-va ngris

Application hosting commands

The deploy CLI family builds and ships apps to the Ngris edge — static sites, managed backends, full-stack apps, and serverless functions. Unlike the tunneling commands (above), nothing keeps running on your machine: each command performs an action against your account and returns. Many accept --json for machine-readable output.

Authenticate

ngris auth --token <token> saves an account API token to the config; every app-hosting command then acts on that account with full dashboard parity. Create the token from the dashboard under Account → Security → Auth token. Key authentication (Preview) covers the tunnel login only: these commands always need a token.

Targeting an app

Each app command targets one application by its UUID with --app <uuid>. To skip the flag, run ngris apps link --app <uuid> once in a project directory — later commands in that directory infer the app automatically.

List & create

ngris apps list lists the account's applications (name, type, live version). ngris apps create <name> --type static|managed|fullstack|function creates an app of any type; add --source git --repo <url> (with optional --branch / --git-token) for a git app, and --root-dir <dir> to build from a monorepo subdirectory. The build settings flags (below) also work on create, so the first build uses them. apps create has no --subdomain: an app gets its address from an endpoint, so create one for it afterwards. For a static site with its address in one step, run ngris deploy <dir> --name <app> --subdomain <sub>.

Deploy & redeploy

ngris deploy [DIR] zips DIR (default .) and deploys it. Target an existing app with --app <uuid>, or pass --name <n> (with an optional --subdomain) to create-and-deploy in one step. --preview builds a not-live candidate; --version <label> tags the deploy. ngris apps redeploy --app <uuid> (alias ngris apps restart) rebuilds and redeploys the app's current source; add --no-activate to build a candidate without going live.

Promote & roll back

ngris apps promote <deployUUID> --app <uuid> makes a preview/candidate deploy live with no rebuild — an instant pointer flip. ngris apps rollback <deployUUID> --app <uuid> reverts to an earlier deploy, also without rebuilding. Run ngris apps deploys --app <uuid> to list the deploy history (version, status, whether it's rollbackable, and the UUID) — those <deployUUID>s are what promote, rollback, and apps logs take.

Logs & status

ngris apps logs <deployUUID> --app <uuid> streams that deploy's build log. When a build succeeded but the app crashes at runtime, ngris apps logs --runtime --app <uuid> tails the running managed pod's stdout/stderr (--tail N to bound it, --previous for the crashed instance). ngris apps status --app <uuid> reports live managed-runtime status — instances, replicas, restarts.

Settings & env vars

ngris apps settings --app <uuid> edits serving settings (--name, --publish-dir, --root-dir, --index) and build settings: --install-command, --build-command and --output-dir (a static app, a full-stack front end), --start-command (a managed app, a full-stack backend; a managed app also takes install and build). An empty value (--build-command='') goes back to detection; the next deploy or ngris apps redeploy uses them. ngris env <list|pull|set|rm> --app <uuid> (or ngris apps env …) manages environment variables; the subcommand comes right after env. Every subcommand takes --env production|preview|all (which deploys get the variable):

  • list prints a table of every variable with its environment, its source (yours, or a connected database) and its value (secret values hidden). --env narrows it to one environment.
  • pull prints, as a dotenv file, the variables a deploy in that environment gets (default production; secret values hidden). --include-db adds the variables your connected databases provide.
  • set NAME=VALUE adds or updates one (default --env all; --secret encrypts it).
  • rm NAME removes it from one environment (default all). A name that isn't set there gives a “not found” error and nothing changes.

Cron jobs

ngris apps cron --app <uuid> lists the app's cron jobs: state, schedule, method, path, next run (UTC), last status, and UUID. add --schedule "0 * * * *" creates one (--path, default /; --method, default GET; optional --name, repeatable --header "Name: value", and --paused). run <job> fires a job now and exits non-zero unless the app answered 2xx/3xx; pause, resume, and rm take the same <job>: the job's UUID, a unique prefix of it, or its name. Schedules are UTC. Cron jobs are a paid-plan feature.

Link & delete

ngris apps link --app <uuid> links the current directory to an app so later commands can omit --app. ngris apps delete <appUUID> deletes an app and all of its deploys.

Same API, programmatically. The deploy CLI wraps the REST /v1/applications* API. CI and automation can call that API directly with an account token — the same operations, same account, same dashboard parity. See the Applications API reference.
Authenticate
# Save an account token (dashboard: /account/security/auth-token)
ngris auth --token <token>
List & create
ngris apps list                                   # list apps (name, type, live version)
ngris apps list --json                            # machine-readable

ngris apps create my-site --type static           # any type: static|managed|fullstack|function
ngris apps create my-api --type managed \
  --source git --repo https://github.com/acme/api --branch main
ngris apps create my-web --type fullstack \
  --root-dir apps/web                       # build from a monorepo subdirectory
Deploy & redeploy
ngris deploy                                 # zip . and deploy (needs --app or a link)
ngris deploy ./dist --app <uuid>
ngris deploy --name my-site --subdomain site # create-and-deploy in one step
ngris deploy --preview --version rc1 --app <uuid>   # not-live candidate, tagged

ngris apps redeploy --app <uuid>                  # rebuild current source (alias: ngris apps restart)
ngris apps redeploy --no-activate --app <uuid>    # candidate, don't go live
Promote & roll back
ngris apps deploys --app <uuid>                  # list deploy history + the UUIDs below
ngris apps promote <deployUUID> --app <uuid>      # make a candidate live (no rebuild)
ngris apps rollback <deployUUID> --app <uuid>     # revert to a previous deploy
Logs & status
ngris apps logs <deployUUID> --app <uuid>         # a deploy's BUILD log
ngris apps logs --runtime --app <uuid>            # running managed app's stdout/stderr
ngris apps logs --runtime --tail 200 --previous --app <uuid>   # crashed instance
ngris apps status --app <uuid>                    # instances, replicas, restarts
Settings & env vars
ngris apps settings --app <uuid> --publish-dir dist --index index.html
ngris apps settings --app <uuid> --root-dir packages/backend
ngris apps settings --app <uuid> --build-command 'npm run build -- --outDir site' --output-dir site
ngris apps settings --app <uuid> --start-command 'node dist/server.js'

ngris env list --app <uuid>                            # table: NAME ENVIRONMENT SOURCE VALUE
ngris env pull --app <uuid> > .env                     # production, dotenv (secret values hidden)
ngris env pull --app <uuid> --env preview --include-db  # what a branch preview gets
ngris env set API_BASE_URL=https://api.example.com --app <uuid>
ngris env set API_KEY=… --secret --env production --app <uuid>
ngris env rm OLD_FLAG --app <uuid>                      # from "all"
Cron jobs
ngris apps cron --app <uuid>                                   # list jobs
ngris apps cron add --schedule "0 * * * *" --path /api/cron --app <uuid>
ngris apps cron add --schedule @daily --method POST --path /tasks/digest \
  --header "X-Job: digest" --name digest --app <uuid>
ngris apps cron run digest --app <uuid>                         # fire now; exit 1 unless 2xx/3xx
ngris apps cron pause 3f2a9c10 --app <uuid>                     # UUID, unique prefix, or name
ngris apps cron resume 3f2a9c10 --app <uuid>
ngris apps cron rm 3f2a9c10 --app <uuid>
ngris apps env set CRON_SECRET=… --secret --app <uuid>        # sent as Authorization: Bearer
Link & delete
ngris apps link --app <uuid>    # later commands in this dir can omit --app
ngris apps delete <appUUID>      # delete the app and all its deploys

Connect to an HTTPS backend

By default the agent forwards to your local service over plain TCP. When that service already speaks TLS — an HTTPS dev server, an internal API, or a backend that demands a client certificate — give the http command an https:// address and the agent connects to it over TLS, verifying the backend certificate. Only the connection between the agent and your backend changes; the public endpoint and its certificate are untouched. Every flag below also works in the config file and via the matching AGENT_LOCAL_SERVICE_* environment variable.

The scheme is the switch

The local address decides the transport. ngris http https://localhost:8443 dials the backend over TLS; an http:// address or a bare host:port stays plaintext and byte-for-byte unchanged. Over TLS the backend certificate is verified against your system trust pool by default, using TLS 1.2 or newer.

Verify a private or self-signed certificate

Point --upstream-ca ./ca.pem at the PEM-encoded CA that signed the backend certificate and the agent trusts that CA instead of the system pool — this is what a self-signed dev backend needs. If nothing verifies the certificate the TLS handshake fails and the request is refused; that is verification working, not a misconfiguration.

Dev-only escape hatch. To skip certificate verification altogether, set local_service_insecure_tls: true in the config file or AGENT_LOCAL_SERVICE_INSECURE_TLS=true in the environment. There is deliberately no command-line flag for it — it turns off all certificate checking for the backend connection and must never be used outside local testing.

Present a client certificate (mTLS)

When the backend requires mutual TLS, hand the agent a client certificate with --upstream-client-cert ./client.pem and its key with --upstream-client-key ./client.key. The two are a pair: setting one without the other is a startup error, so the agent stops with a clear message rather than connecting without the certificate it was told to present.

Override the SNI / certificate hostname

--upstream-server-name backend.internal sets both the SNI sent during the handshake and the name the backend certificate is checked against. It defaults to the host portion of the local address, so reach for it only when the certificate names something other than what you dial — for instance when you connect to an IP address but the certificate was issued for an internal hostname.

Rewrite the Host header sent to the backend

By default the agent forwards the incoming request's Host header to the backend unchanged — that is your public Ngris endpoint hostname. A backend that routes on Host, such as a shared virtual host or a public CDN, needs its own name instead. --upstream-host rewrite sends the backend's own host (the host portion of the local address), and --upstream-host backend.example.com sends any literal value you name. Left unset it stays passthrough, so the default forwarding path is byte-for-byte unchanged.

Backends on another host

The backend does not have to be on localhost: the agent reaches any host it can route to, so ngris http https://internal-api.corp:8443 forwards your public endpoint straight to an internal service. Two conveniences make remote and vhost backends easy:

  • The port is optional. A schemed address defaults its port from the scheme — https:// dials :443 and http:// dials :80 — so ngris http https://internal-api.corp connects to :443 with nothing more to type. Add an explicit :port (for example :8443) whenever the backend listens elsewhere; an explicit port is always honored.
  • The forwarded Host is yours to set. By default the agent passes the incoming request's Host header through unchanged — your public Ngris endpoint hostname — while the TLS SNI follows the upstream host. A plain backend ignores Host; one that routes on it (a shared virtual host or a public CDN) needs the name it expects. Reach for --upstream-host (above): ngris http https://example.com --upstream-host rewrite forwards to a public vhost and the backend receives Host: example.com.
Read at connect. The scheme and these flags are applied when the agent starts and cached for the session — restart ngris to change them. The plaintext http:// and bare host:port paths are unaffected.
Forward to an HTTPS backend
# Verified against the system trust pool (TLS 1.2+)
ngris http https://localhost:8443

# Verify a private / self-signed backend cert
ngris http https://localhost:8443 --upstream-ca ./ca.pem

# Mutual TLS — present a client cert (both flags required together)
ngris http https://localhost:8443 \
  --upstream-client-cert ./client.pem \
  --upstream-client-key ./client.key

# Override the SNI / cert hostname (default: host of the address)
ngris http https://10.0.0.5:8443 --upstream-server-name backend.internal

# Rewrite the Host header for a vhost / CDN backend (port defaults to :443)
ngris http https://example.com --upstream-host rewrite

# A backend on another host (port defaults from the scheme; :8443 here is explicit)
ngris http https://internal-api.corp:8443 --upstream-ca ./corp-ca.pem

# Dev only: skip verification (config / env — there is no CLI flag)
AGENT_LOCAL_SERVICE_INSECURE_TLS=true ngris http https://localhost:8443

Files on disk

What the agent reads and writes on your machine.

File What it is
config.yaml The agent reads config.yaml from your user config directory (older versions read one in the working directory first): ~/.config/ngris/config.yaml on Linux, ~/Library/Application Support/ngris/config.yaml on macOS, %AppData%\ngris\config.yaml on Windows. --config <file> names one explicitly. ngris auth --token saves the token (mode 0600) to the --config file, else to ./config.yaml if it exists, else to the user config directory.
.ngris/project.json The app a project directory is linked to, written by ngris apps link (and when ngris deploy --name creates an app).
Log file Only when logging.file is set.
Response spool Temporary files for responses waiting for a slow client (Large transfers), in the ngris/spool directory of your user cache directory: ~/.cache/ngris/spool on Linux, ~/Library/Caches/ngris/spool on macOS, %LocalAppData%\ngris\spool on Windows. See below.

Response spool

When a public client reads a response slower than your service writes it, the agent keeps reading your service and holds the difference in a spool, then sends it as the client catches up. Each response uses up to 1 MiB of memory, then a temporary file of up to 1 GiB. All responses of one agent together use at most 64 MiB of memory and 2 GiB of disk, and one client IP (for IPv6, its /64) at most an eighth of each. When the spool is full, the agent stops reading your service for that response until its client catches up.

  • Location. ngris/spool in your user cache directory (on Linux, $XDG_CACHE_HOME/ngris/spool when XDG_CACHE_HOME is set); without a user cache directory, ngris-agent-spool in the temp directory. The directory must be the agent's own — a real directory, not a symlink (on Linux and macOS: mode 0700, owned by the agent's user); otherwise the agent uses a new private ngris-spool-* directory in the temp directory, and if it can't create one, spools stay in memory. The agent logs the directory it picked when it starts (a [spool] line).
  • Files. On Linux and macOS a spool file is deleted right after it is created, so the directory looks empty even while the spool uses disk space; the space is freed when the response ends or the agent exits. On Windows the spool-* files are deleted when they are closed or the agent exits. At start the agent removes any spool files left in the directory.
  • tmpfs. If the directory is on tmpfs or ramfs (Linux), spool files are memory: they count against the 64 MiB memory budget, not the 2 GiB disk one.
Default locations
# Config (when there is no ./config.yaml)
~/.config/ngris/config.yaml                     # Linux
~/Library/Application\ Support/ngris/config.yaml # macOS
%AppData%\ngris\config.yaml                     # Windows

# Response spool (up to 64 MiB memory + 2 GiB disk per agent)
~/.cache/ngris/spool                            # Linux
~/Library/Caches/ngris/spool                    # macOS
%LocalAppData%\ngris\spool                      # Windows

Troubleshooting

“This ngris agent … is too old”

The edge refused an agent that doesn't support flow control (every agent before the current release), and the agent stopped. A message saying the agent “is older than the minimum supported version” means the same: update the agent and start it again. See Updating the agent.

“This ngris CLI … is older than the minimum supported version” (426)

A CLI command such as ngris deploy or ngris apps list was refused because the agent is older than the minimum version Ngris supports. The command prints the reason and stops, for example error: upgrade required (426): This ngris CLI (v<your version>) is older than the minimum supported version <minimum>. Update: https://ngris.com/install; older releases print it as HTTP 426: …. Update the agent and run the command again. See Updating the agent.

Uploads fail with 408, 400 or 502

408: the client sent its body too slowly (under 64 KiB per minute) or stopped for 60 seconds. 400: the body was shorter than its Content-Length. 502: your service stopped reading the body for 2 minutes. See Large transfers & timeouts.

A long download stops

The edge ends a response when no bytes reach the client for 60 seconds, or when the client takes under 64 KiB in 60 seconds while data waits for it. See Large transfers & timeouts.

Port already in use

Change the local port or stop the conflicting process.

Firewall/Corporate proxy

Allow outbound QUIC/HTTPS to Ngris regions or switch transport.

Docker/K8s: Endpoint fails to connect

Ensure the agent can reach your service by name and port.

Free the port
# macOS/Linux
lsof -i :3000
kill -9 <pid>

# Windows
netstat -ano | findstr :3000
taskkill /PID <pid> /F
Switch transport
# Use HTTP/2 for local connection (gRPC, etc.)
ngris http 3000 --http2

# Use HTTP/3 QUIC (experimental)
ngris http 3000 --http3
Test container connectivity
# Test connectivity from within container
docker exec -it tunnel-container ping app
kubectl exec -it ngris-pod -- wget -qO- http://web-service:8080/health

FAQ

Is traffic encrypted?

Yes, end-to-end encryption is enforced. The connection between your agent and our edge servers uses mutual TLS. Public endpoints serve HTTPS with auto-renewed certificates via ACME. Data in transit is always protected.

Can I self-host the Ngris server?

No. Ngris is a hosted SaaS only — there is no on-premise, private-cloud, or self-hosted edition. You run the small agent (or the Kubernetes operator) next to your service; the edge, control plane, and dashboard are operated by Ngris.

What happens when my endpoint disconnects?

If the agent disconnects, the public endpoint returns a 502 Bad Gateway error. Upon reconnection, the connection resumes automatically if using persistent credentials. For critical services, implement health checks and alerts via the API.

How long are request logs retained?

How long traffic logs are kept depends on your plan: GET /v1/account/plan returns it as plan_limits.traffic_log_retention_days, and every GET /v1/traffic/logs response carries your account's window as retention_days. On a paid plan you can keep them longer with the extended log retention add-on, billed per extra day.

Was this helpful?