Skip to content
varsafe
Esc
navigateopen⌘Jpreview
On this page

Getting Started

Install the varsafe CLI, log in, and get from zero to injected secrets in under five minutes.

CLI v7.2.7

By the end of this page you will have the CLI installed, a secret stored in varsafe, and that secret injected into a running process — without a plaintext .env file on disk.

Prerequisites

You need

A varsafe account — sign up free

Supported

Linux and macOS, x64 and arm64, on both glibc and musl — including Raspberry Pi, Graviton and ARM CI runners. Windows works via WSL.

From zero to injected secrets

Install the CLI

The CLI ships as a self-contained binary — it is not published on npm. The installer places it in ~/.varsafe/bin and adds that directory to your shell’s PATH:

curl -fsSL https://varsafe.dev/install.sh | bash

Open a new shell (or source your shell config), then verify:

varsafe --version

The CLI keeps itself current — run varsafe update any time to install the latest release.

Log in

varsafe login

This opens your browser and shows a short confirmation code in the terminal. Approve the request in the browser — the code on screen must match the one in your terminal — and the CLI completes automatically. No password ever passes through the terminal.

If a browser isn’t available (headless machine, container, CI runner), use an API token instead — see CLI authentication.

Point at a project and environment

varsafe use -p my-api -e development

Saved locally, so the commands that follow do not need -p or -e.

Store a secret

Secret values are never accepted as arguments — they would leak into shell history and ps output. Pick whichever input suits you:

varsafe set DATABASE_URL

Omit the value and the CLI asks for it with masked input. Best at a terminal.

printf %s 'postgres://app:app@localhost:5432/app' | varsafe set DATABASE_URL --stdin

Best in scripts, where there is nothing to type into.

varsafe set TLS_PRIVATE_KEY --from-file ./server.key

Best for multi-line values such as certificates and private keys.

varsafe set API_KEY --from-env BUILD_API_KEY

Best in CI, where the value already exists as a variable.

Then confirm it landed:

varsafe list          # keys only — values stay masked
varsafe get DATABASE_URL   # revealing a value is audited

Inject it into your app

This is the core workflow. varsafe run fetches your secrets over TLS and injects them as environment variables into the child process — never written to disk, gone when the process exits:

varsafe run -- npm run dev

Your app sees DATABASE_URL in its environment; your shell and filesystem never do. To prove it:

varsafe run -- sh -c 'echo "DATABASE_URL is ${DATABASE_URL:+set}"'

Expected output: DATABASE_URL is set.

Recap

Command What it does
varsafe use -p <project> -e <env> Points the CLI at a project and environment, saved locally
varsafe set KEY Stores a secret, prompting for the value with masked input
varsafe list Shows the keys in scope, values masked
varsafe run -- <command> Runs your app with secrets in its environment, nothing on disk
varsafe export -o .env Writes an encrypted file, for tools that insist on one

Everything else is a variation on these. varsafe status tells you which credential, project, and environment the next command will use.

Export when a file is required

Some tools insist on a .env file. varsafe export writes one — encrypted by default, so the file is useless without your environment’s keys:

varsafe export -o .env

Plaintext requires an explicit opt-in with --plain. See encrypted .env files and the export workflow for formats and options.

The same flow, scripted

The script below uses the automation-style login: $VARSAFE_TOKEN holds an API token (in CI, from your CI secret store), $VARSAFE_PROJECT names the project, and $VARSAFE_API_URL points at the API — in normal usage you omit --api-url and the CLI talks to https://api.varsafe.dev.

# docs-test: local-stack
# Store a secret and read it back.
set -euo pipefail

# No `varsafe login` here: VARSAFE_TOKEN is already in the environment and the
# CLI picks it up automatically. That is the only auth path that works on a
# headless runner, where `login` has no keychain to persist to.

# Set your default project and environment once
varsafe use -p "$VARSAFE_PROJECT" -e development

# Store a secret. The value comes in on stdin — passing it as an argument is
# refused, because arguments leak via shell history and `ps`.
printf %s 'postgres://app:app@localhost:5432/app' | varsafe set DATABASE_URL --stdin

# List keys (values stay hidden by default)
varsafe list

# Read a single value (pipe-friendly)
varsafe get DATABASE_URL > /dev/null

echo "OK:quickstart"

And the injection flow end to end — note the child process assertion proving the secret arrived:

# docs-test: local-stack
# Inject secrets into a child process as environment variables — no file
# is ever written to disk.
set -euo pipefail

# VARSAFE_TOKEN in the environment authenticates every command below; `varsafe
# login` is neither needed nor possible on a headless runner (no keychain).
varsafe use -p "$VARSAFE_PROJECT" -e development

printf %s 'vsafe_example_run_inject_value' | varsafe set API_KEY --stdin

# The child process sees API_KEY; your shell never does.
varsafe run -- sh -c 'test -n "$API_KEY"'

# Only inject secrets matching a pattern
varsafe run --include 'API_*' -- sh -c 'test -n "$API_KEY"'

echo "OK:run-inject"

Project setup

The dashboard gives you a full overview of your secrets across all projects and environments:

varsafe dashboard
varsafe dashboard

Signing up already created a team, a project, and three environments — which is why the commands above worked without any setup. You only add more when you outgrow them: another project per repository, another environment for a QA or demo stage.

Environment -e slug Purpose Protected
Development development Local development No
Staging staging Pre-production testing No
Production production Live systems Yes

The slug is what you pass to -e, and it is also the address your secrets are stored under. Renaming an environment is safe; changing its slug re-points everything that referred to it, including API-token environment allowlists — so treat the slug as fixed once secrets exist.

Protected means writes ask for confirmation: varsafe set and varsafe unset require you to type the environment name, or pass -y in automation. Production starts protected; you can protect any environment in its settings.

Two things the dashboard does that the CLI does not:

  • Bulk import — on the Secrets page, “Add secrets” → “Bulk” takes a pasted or drag-dropped .env file, which is the fastest way to move an existing project in.
  • Seeing everything at once — every project and environment side by side, plus the audit trail of who read or changed what.

Team collaboration

Invite teammates from the Teams page (“Invite member”) and assign each a role: owner, admin, developer, operator, viewer, or billing. Roles determine read/write access per environment — production is protected by default, restricting writes to owners and admins. See the roles reference for the full permissions matrix.

CI/CD integration

For automated pipelines, use an API token instead of a personal session:

  1. Go to your team settings in the dashboard
  2. Navigate to “API Tokens”
  3. Create a new token
  4. Add it to your CI/CD secrets as VARSAFE_API_TOKEN
# GitHub Actions example
- name: Deploy with secrets
  run: varsafe run -p my-api -e production -- ./deploy.sh
  env:
    VARSAFE_API_TOKEN: ${{ secrets.VARSAFE_API_TOKEN }}

The CLI picks up VARSAFE_API_TOKEN automatically — no varsafe login step needed. See CI/CD usage for GitLab CI and more.

Next steps