[Bug]: `t3 update` / `t3 service install` re-render the service unit and drop every user environment variable, including the documented Bitbucket credentials
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 23k
- Forks
- 5.9k
- Avg merge
- 11h 14m
- Merged PRs (30d)
- 357
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
- On macOS, run
t3 service install. - Follow docs/user/source-control.md for Bitbucket: add
T3CODE_BITBUCKET_EMAILandT3CODE_BITBUCKET_API_TOKENto the service's environment. The only place launchd takes environment for this job isEnvironmentVariablesin~/Library/LaunchAgents/com.t3tools.t3code.service.plist, so add them there andlaunchctl bootout/bootstrap. - Confirm Settings → Source Control shows Bitbucket authenticated.
- Run
t3 update(or runt3 service installagain to "repair" the service, as docs/user/background-service.md suggests).
Expected behavior
Environment the user added to the service survives an update or reinstall, or there is a documented place to put service environment that t3 does not overwrite.
Actual behavior
The plist is re-rendered from a fixed template and the user's entries are gone. Bitbucket reverts to "Available. Set T3CODE_BITBUCKET_EMAIL and T3CODE_BITBUCKET_API_TOKEN on the server". The same happens to T3CODE_PORT / T3CODE_HOST pins, so a service pinned off the default port silently moves back to port scanning after an update.
Mechanics:
renderBootServicePlist(apps/server/src/cloud/bootService.ts~L134) emits exactly PATH,T3CODE_HOME, andT3_BOOT_SERVICE_UNIT.renderBootServiceUnitdoes the same for systemd.install()unconditionally runswriteDurably(unitPath, manager.render(plan))(~L885). The only value read back from an existing unit isT3CODE_HOMEviabootServiceBaseDirOf(~L64).t3 updatecallsinstall()on every version switch (apps/server/src/cli/update.ts~L559, comment: "The unit is rewritten either way").- On Linux a
systemctl --user editdrop-in would survive this. launchd has no override mechanism, so on macOS there is nowhere to put service environment that t3 will not delete. - docs/user/source-control.md tells Bitbucket users to export the variables "in the server's environment" and never mentions the background service; docs/user/background-service.md never mentions environment. Bitbucket is the only provider that needs environment, because it has no CLI to hold credentials.
Suggested fixes (any one would do):
- Preserve unknown
EnvironmentVariables/Environment=entries when re-rendering an existing unit. - Have the launcher read an optional env file (e.g.
$T3CODE_HOME/service.env) and document it. - Store Bitbucket credentials in the server's existing secrets store, settable from Settings → Source Control, so no environment is needed.
Impact
Blocks a workflow (Bitbucket source control cannot be kept working on a macOS background service across updates)
Version or commit
0.0.42 → 0.0.43-nightly.20260919.1962
Environment
macOS 26.6 (Darwin 25.6.0), arm64, background service via launchd
Logs or stack traces
# after `t3 update`, the regenerated plist EnvironmentVariables dict contains only:
PATH, T3CODE_HOME, T3_BOOT_SERVICE_UNIT
Workaround
Keep the variables out of t3's plist: a separate user LaunchAgent that runs launchctl setenv T3CODE_BITBUCKET_EMAIL … ; launchctl setenv T3CODE_BITBUCKET_API_TOKEN … ; launchctl kickstart -k gui/$UID/com.t3tools.t3code.service at login. The service inherits the domain environment and t3 never touches that file.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with apps/server/src/cloud/bootService.ts, especially renderBootServicePlist, renderBootServiceUnit, bootServiceBaseDirOf, and install(), then trace the reinstall call in apps/server/src/cli/update.ts. Compare how existing launchd and systemd units are read and rewritten. Done means user-provided service environment survives update and reinstall on both platforms, with the relevant background-service and source-control documentation updated.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli, devops, infrastructure
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100