Dotfiles Sync (dotfiles-sync)
Dotfiles Sync (dotfiles-sync)
Section titled “Dotfiles Sync (dotfiles-sync)”Syncs local Git, SSH, GPG, npm, gh, cargo, pip, yarn/pnpm config files into the devcontainer. Optionally syncs cloud credentials (AWS, kube, Docker, gh OAuth token) — opt-in only. Works on macOS, Linux, Windows (WSL and native), GitHub Codespaces, Gitpod, and DevPod. Uses a merge strategy for established files and a copy-if-absent strategy for new ones — never overwrites existing values, safe alongside cloud platform native auth and GPG signing.
That’s it. The feature auto-detects the environment and adapts its behavior.
With custom username
Section titled “With custom username”Match this to your
remoteUserindevcontainer.json.
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
username | string | node | Container username that receives synchronized config files |
syncGhAuth | boolean | false | Copy ~/.config/gh/hosts.yml (GitHub OAuth token used by gh CLI) into the container’s $HOME. Skipped on cloud environments (Codespaces / Gitpod / DevPod inject their own token). When false, the file is bind-mounted into /tmp/dotfiles-sync (Feature mounts cannot be conditional) but never copied to $HOME and never read by anything else. Prefer the github-dev feature with GH_TOKEN for fine-grained PATs. |
syncAwsConfig | boolean | false | Sync ~/.aws/config (profiles only — ~/.aws/credentials is never synced). |
syncKubeConfig | boolean | false | Sync ~/.kube/config (cluster credentials and tokens). Skipped on cloud environments. |
syncDockerConfig | boolean | false | Sync ~/.docker/config.json (registry auth tokens). Skipped on cloud environments. |
What Gets Synced
Section titled “What Gets Synced”Always synced
Section titled “Always synced”| Local Path | Final Target | Strategy | Purpose |
|---|---|---|---|
~/.gitconfig | ~/.gitconfig | Merge via git config | Git user configuration |
~/.gitignore_global | ~/.gitignore_global | Copy-if-absent | Personal global gitignore |
~/.config/git/ignore | ~/.config/git/ignore | Copy-if-absent | XDG global gitignore |
~/.config/git/attributes | ~/.config/git/attributes | Copy-if-absent | XDG global gitattributes |
~/.config/git/config-* | ~/.config/git/config-* | Copy-if-absent | Modular git includes |
~/.ssh | ~/.ssh | Per-file merge | SSH keys, config, known_hosts |
~/.gnupg | ~/.gnupg | Copy-if-absent (skipped on cloud) | GPG keys for commit signing |
~/.npmrc | ~/.npmrc | Merge line-by-line | npm registry auth |
~/.yarnrc.yml | ~/.yarnrc.yml | Copy-if-absent | yarn registries / settings |
~/.config/pnpm/rc | ~/.config/pnpm/rc | Copy-if-absent | pnpm settings |
~/.config/gh/config.yml | ~/.config/gh/config.yml | Copy-if-absent | gh CLI preferences (no token) |
~/.cargo/config.toml | ~/.cargo/config.toml | Copy-if-absent | Cargo registries / profiles |
~/.config/pip/pip.conf | ~/.config/pip/pip.conf | Copy-if-absent | pip index URLs |
Opt-in (sensitive)
Section titled “Opt-in (sensitive)”| Local Path | Option | Notes |
|---|---|---|
~/.config/gh/hosts.yml | syncGhAuth | GitHub OAuth token used by gh. The file is bind-mounted into /tmp/dotfiles-sync unconditionally (Feature mounts cannot be gated on options) but only copied to $HOME when syncGhAuth: true. Skipped on cloud environments. For fine-grained PATs, prefer github-dev + GH_TOKEN. |
~/.aws/config | syncAwsConfig | AWS profiles. ~/.aws/credentials (long-lived access keys) is not bind-mounted and never synced. |
~/.kube/config | syncKubeConfig | Kubernetes cluster credentials. Skipped on cloud environments. |
~/.docker/config.json | syncDockerConfig | Docker registry auth tokens. Skipped on cloud environments. |
Never synced
Section titled “Never synced”~/.aws/credentials— never bind-mounted, long-lived access keys are too risky to copy into a container.- Shell rc files (
~/.bashrc,~/.zshrc,~/.profile) — would conflict with the container’s own shell setup. Use VS Code’s nativedotfiles.repositoryfor that.
GitHub authentication
Section titled “GitHub authentication”gh CLI authentication is off by default. Pick whichever fits your workflow:
-
github-devfeature +GH_TOKEN(recommended for fine-grained scope): -
Sync your local
gh auth logintoken (syncGhAuth: true):The token is copied with
chmod 600and only if~/.config/gh/hosts.ymldoes not already exist in the container. Skipped on Codespaces / Gitpod / DevPod (the platform injects its own token).Security note — because DevContainer Feature
mountscannot be conditional on options,~/.config/gh/hosts.ymlis bind-mounted into/tmp/dotfiles-sync/.config/gh/hosts.ymlwhether or not you opt in. Nothing reads that path unlesssyncGhAuth: true, but if your threat model considers any in-container exposure unacceptable, use approach 1 or 3 instead. -
gh auth logininside the container — token stays in the container only.
Merge Strategy
Section titled “Merge Strategy”| File | Strategy |
|---|---|
.gitconfig | Applies source keys via git config — skips keys already present in target |
.npmrc | Appends key=value lines absent from target |
.ssh/config | Appends Host blocks not already present |
.ssh/known_hosts | Appends host entries not already present |
.ssh keys | Copies files only if destination does not exist |
.gnupg | Copied on local/WSL; skipped on cloud environments (see below) |
| All other files (gitignore_global, gh/config.yml, cargo, pip, yarn, pnpm, …) | Copy-if-absent — never overwrites an existing target |
Cloud environment protection
Section titled “Cloud environment protection”On GitHub Codespaces, Gitpod, and DevPod, the platform manages git authentication and GPG signing. The following .gitconfig keys are never overwritten if already set by the platform:
| Key | Reason |
|---|---|
credential.helper | Platform injects its own token-based credential helper |
user.name | Set from your platform profile |
user.email | Set from your platform profile |
user.signingkey | Platform uses its own signing key |
gpg.program | Codespaces injects /.codespaces/bin/gh-gpgsign — overwriting it breaks signing |
gpg.format | Managed by platform |
commit.gpgsign | Managed by platform |
tag.gpgsign | Managed by platform |
GPG signing on cloud environments
Section titled “GPG signing on cloud environments”.gnupg is not synced on cloud environments because:
- GitHub Codespaces: uses
/.codespaces/bin/gh-gpgsign, a GitHub-managed proxy. Commits are signed with a GitHub key and show as “Verified” on GitHub.com. To enable it: GitHub Settings → Codespaces → GPG verification. - Gitpod: manages its own signing mechanism.
- DevPod: when remote, no local GPG agent is available.
Importing local GPG keys into a cloud environment would conflict with the platform proxy and break signing. On local and WSL, .gnupg is synced normally.
SSH Agent Forwarding
Section titled “SSH Agent Forwarding”SSH agent forwarding works out of the box via VS Code native mechanism.
For optimal reliability across container rebuilds, configure a stable socket on your host:
macOS / Linux (zsh / bash) — add to your shell rc:
macOS with Keychain:
Socket detection priority at runtime:
- Stable socket (
/tmp/dotfiles-sync/.ssh/agent.sock) if mounted - VS Code native forwarding (
$SSH_AUTH_SOCK) - Legacy
/ssh-agent
Platform Notes
Section titled “Platform Notes”macOS / Linux
Section titled “macOS / Linux”Works out of the box. $HOME is always set and Docker bind mounts resolve correctly.
WSL (Windows Subsystem for Linux)
Section titled “WSL (Windows Subsystem for Linux)”Works out of the box. Docker Desktop resolves WSL paths transparently.
Windows (Docker Desktop, no WSL)
Section titled “Windows (Docker Desktop, no WSL)”Works in most cases. Docker Desktop automatically translates C:\Users\<name> paths from bind mounts into the container. However:
HOMEmust be defined on the host. Most Windows setups have it, but if onlyUSERPROFILEis set (noHOME), the bind mounts will silently fail — the staging directory/tmp/dotfiles-sync/will be empty and no files will be synced. In that case, addHOMEto your environment variables with the same value asUSERPROFILE.- CRLF line endings — if
core.autocrlf=trueis set on your Windows Git install,.gitconfigand.npmrcon disk may contain CRLF. The.gitconfigmerge usesgit config --listwhich normalizes line endings correctly. The.npmrcmerge reads the file line-by-line via bash which also handles CRLF, but extra\rcharacters may appear in values — if npm auth fails, rundos2unix ~/.npmrcinside the container. - SSH agent forwarding — Docker Desktop does not forward the Windows OpenSSH agent socket into containers. SSH auth inside the container will rely on copied key files (
.ssh/id_*) rather than a live agent.ssh-add -lwill likely showCould not open a connection to your authentication agent— this is expected. Key-based operations (git clone, push) will still work via the copied keys.
GitHub Codespaces
Section titled “GitHub Codespaces”The feature auto-detects Codespaces via CODESPACES=true. Protected keys are preserved, and .gnupg is not synced (platform manages GPG signing). See Cloud environment protection above.
To get signed commits on Codespaces without managing your own key: enable GitHub Settings → Codespaces → GPG verification. GitHub will sign commits on your behalf using a GitHub-managed key — they will show as “Verified” on GitHub.com.
Gitpod
Section titled “Gitpod”Auto-detected via GITPOD_WORKSPACE_ID. Same cloud protection as Codespaces applies.
DevPod
Section titled “DevPod”Auto-detected via DEVPOD=true or DEVPOD_WORKSPACE_ID. When running on a remote provider (cloud VM), no local files are available — the staging directory will be empty and sync is skipped gracefully. When running locally (Docker), the feature behaves like a standard local devcontainer.
How It Works
Section titled “How It Works”- Build time (
install.sh): Creates directory structure and installs sync scripts - Container start (
postStartCommand): Merges files from staging to user home - Shell startup (
/etc/profile.d/): SSH agent detection + one-time sync fallback
Troubleshooting
Section titled “Troubleshooting”npm auth failing
Section titled “npm auth failing”If empty: rebuild the container after verifying the host file exists.
Git config not appearing
Section titled “Git config not appearing”SSH permission denied
Section titled “SSH permission denied”Migrating from
local-mounts? This feature is the successor tolocal-mounts. Replaceghcr.io/helpers4/devcontainer/local-mounts:1withghcr.io/helpers4/devcontainer/dotfiles-sync:1— options and behavior are identical.
Version History
Section titled “Version History”- v1.0.2: Added
syncGhAuthopt-in to copy~/.config/gh/hosts.yml(GitHub OAuth token used byghCLI) into$HOME. Defaultfalse, skipped on cloud environments. The file is bind-mounted into/tmp/dotfiles-syncregardless (Featuremountscannot be conditional) but only copied to$HOMEwhen the option is enabled. For fine-grained PATs prefer thegithub-devfeature withGH_TOKEN. - v1.0.1: Stop bind-mounting the
~/.config/ghdirectory. Only~/.config/gh/config.yml(CLI preferences) is mounted. Added 3 opt-in booleans for sensitive files:syncAwsConfig,syncKubeConfig,syncDockerConfig— all defaultfalseand skipped on cloud environments. Added low-risk dotfiles (gitignore_global, git/ignore, git/attributes, yarnrc.yml, pnpm/rc, cargo/config.toml, pip/pip.conf) with copy-if-absent strategy.~/.aws/credentialsis never bind-mounted. - v1.0.0: Initial release — successor to
local-mounts. Multi-environment detection (macOS, Linux, WSL, Codespaces, Gitpod, DevPod), merge strategy for all config files, GPG skip on cloud environments, configurable source paths.