Sync to Vercel
A Vercel build reads environment variables that Vercel already holds — there is
no earlier point at which seekrit could inject them. So seekrit stays the source
of truth and pushes each secret into the project as an encrypted environment
variable, for the deployment targets the binding names.
| At a glance | |
|---|---|
| What seekrit writes | Project environment variables, type: encrypted |
| Addressed by | Vercel project id (prj_…) or name, plus one or more targets |
| Connection carries | The API token, and a team id for team-owned projects |
| Token permission | A token with access to the project — Vercel has no per-resource scope |
| Takes effect | Next build. Vercel injects at build and run time from its own copy |
| Value visibility | Encrypted at rest by Vercel, and readable by anyone who can read the project's env vars |
New to sync? Read Third-party sync first — the decryption grant, name mapping, deletions, and failure handling are the same on every destination.
1. Create a Vercel token
In Vercel, go to Settings → Tokens and create a token with access to the
project you want to sync into. If the project belongs to a Vercel Team, note the
team id (team_…) — a personal-scope token can't write to a team project, and
Vercel reports that as a bare 403.
Give the token an expiry you will actually notice, and name it after the connection. Vercel tokens carry their creator's access, so the least-privilege setup is a machine user added to only the projects it syncs.
2. Add the connection
In the dashboard, open Sync → Add connection, choose Vercel, paste the token, and set the team id if you have one. The token is encrypted in your browser before it is sent.
Or from the CLI. The token is read from stdin — never a flag, so it can't end up in your shell history — and is wrapped to the connection's public key before it is sent:
printf '%s' "$VERCEL_TOKEN" | seekrit sync connect \
--name acme-production --team-id team_abc
Vercel is the default provider, so --provider vercel can be left off. The team
id is sent as ?teamId= on every request the connection makes; omit it for a
personal account.
Check it can reach the project before you bind anything to it:
seekrit sync verify acme-production --project prj_abc
Verify fetches the project (GET /v9/projects/{id}), which proves three things
at once: the token is valid, it reaches this project, and the team id is right
or not needed.
3. Bind an environment
Pick the application environment to sync and the Vercel project and targets
(production, preview, development). This is the step that shows the
decryption disclosure and creates the key grant, so it has to be done by someone
who can already read the environment.
seekrit sync enable \
--connection acme-production \
--app storefront --env production \
--project prj_abc \
--target production \
--acknowledge-decryption
--target takes a comma-separated list and defaults to production. A binding
writes each variable to every target it names, which is how one seekrit
environment feeds both production and preview from a single binding.
seekrit pushes once immediately, then on every change.
Preview deployments and one branch
--git-branch restricts preview writes to a single git branch. Vercel only
honors it when preview is among the targets, so seekrit sends it only then —
a production-only binding that carried a branch would be rejected outright.
seekrit sync enable --connection acme-production \
--app storefront --env staging \
--project prj_abc --target preview --git-branch staging \
--acknowledge-decryption
This pairs naturally with branch environments: point a
seekrit branch environment at a Vercel preview target with its git branch set,
and each PR gets its own overlay downstream. When the branch expires its binding
goes with it, and seekrit stops pushing — the values already on Vercel stay
where they are.
How a push behaves
- One request per name,
POST /v10/projects/{id}/env?upsert=true. Vercel's upsert is create-or-replace, so a run never needs to read the project's current values first. - Values are stored as
encrypted. Vercel can read them — that is inherent to pushing anywhere, and is exactly what the sync key grant authorizes. - A removal costs a listing. Deleting needs Vercel's internal env-var id, so a run with removals lists the project's variables once, then deletes each. Only records whose targets overlap this binding's are touched, so two bindings can point at one project with different targets and neither will delete the other's variables. A variable already gone counts as deleted rather than failing forever.
- A run is capped at 500 operations. Beyond that it reports that it has more to do and the engine re-runs immediately, so a very large first sync is slower rather than silently partial.
- A 429 stops the whole run. seekrit honors Vercel's
Retry-Afterrather than its own backoff curve; what already landed is reported, and the rest retries.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
403 on every name | The project is team-owned and the connection has no team id | Add --team-id team_…, or re-create the connection with it |
403 after it was working | The token expired, or its owner lost access to the project | Mint a new token and re-create the connection |
404 on the project | Wrong project id, or the project was deleted or renamed | Check prj_… in the project's Settings; seekrit sync verify catches this before a binding exists |
400 mentioning gitBranch | A branch was set on a binding whose targets don't include preview | Add preview to --target, or drop --git-branch |
429, run reported partial | Vercel rate-limited the account | Nothing to do — seekrit backs off and retries the rest |
| Values pushed, site unchanged | Vercel injects env vars at build time | Redeploy. seekrit does not trigger Vercel builds |
See also
- Third-party sync — the shared model, naming and filtering, deletions
- Branch configs — per-PR environments that pair with Vercel previews
- CLI reference — every
seekrit syncflag