Repository navigation
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_UNITWorkaround
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.
Activity
juliusmarminge commentedon Sep 19, 2026
Thanks for the precise write-up — this checks out on current
main.t3 service installandt3 updatealways re-render the unit from a closed template (renderBootServicePlist/renderBootServiceUnitinapps/server/src/cloud/bootService.ts) and write it withwriteDurably. The only value read back from an existing unit isT3CODE_HOME. ExtraEnvironmentVariables/Environment=entries are discarded.t3 updatedoes this on every version switch (apps/server/src/cli/update.ts).Bitbucket is the sharp edge because it is env-only (
T3CODE_BITBUCKET_EMAIL/T3CODE_BITBUCKET_API_TOKEN/T3CODE_BITBUCKET_ACCESS_TOKENinBitbucketApi.ts) and Settings → Source Control has no place to store those credentials. The same wipe also dropsT3CODE_PORT/T3CODE_HOST. Linux can keep extras in asystemctl --user editdrop-in; launchd has no equivalent, so on macOS there is currently no supported place for service environment that survives an update.This is not a duplicate of #5840 / #9022 (desktop Dock launch vs shell profile). Same credentials, different surface.
Workaround (as you described): keep the variables out of t3’s plist — a separate user LaunchAgent that
launchctl setenvs them and kickstartsgui/$UID/com.t3tools.t3code.service.Likely fix: an optional env file under T3 home that the launcher merges, documented from both the background-service and source-control guides. Preserving unknown plist/unit keys is more fragile. Putting Bitbucket in the secrets store + Settings is the better long-term product, but it does not cover port/host pins and is a larger change.
Accepting as a bug. No further info needed.
cestercian commentedon Sep 19, 2026
Taking this — will preserve user environment variables across
t3 update/t3 service installunit re-renders.gamelaster commentedon Oct 7, 2026
I would add to this, that in case I want to install T3 service, it would be nice to have
--hostparameter, so I can configure, if it is only for local mode, or for 0.0.0.0/tailscale.fix(server): keep background-service env in a T3 home file