This DDEV add-on installs GitHub Copilot CLI inside the DDEV web container and automatically shares your host GitHub CLI and Copilot configuration — including authentication and Copilot settings — with no additional setup required.
Once installed, running copilot inside ddev ssh or ddev exec uses a writable copy of your host configuration and a token derived from your host gh authentication.
- DDEV >= v1.24.0
- GitHub CLI (
gh) installed and authenticated on the host — the host is where the token is resolved, sogh auth statusmust succeed there
ddev add-on get e0ipso/ddev-assistant-copilot
ddev restartAfter installation, commit the .ddev directory to version control.
- Installs GitHub CLI into the container image via the official apt repository, on
$PATHfor every shell - Installs GitHub Copilot CLI on every start via
npm install -g @github/copilotinto~/.local/bin(warns and continues if npm install fails) - Seeds host configuration on start: your host
~/.config/gh/and~/.copilot/trees are mounted read-only under~/.cred-seed/, then mirrored into the writable container directories on every restart:~/.config/gh/— GitHub CLI configuration and authentication (e.g.hosts.yml)~/.copilot/— Copilot CLI configuration (e.g.config.json, hooks)
- Forwards authentication from the host keychain — the token is resolved on the host, where the OS keychain lives, and handed to the container on a read-only mount. See How authentication forwarding works.
- Exports
COPILOT_GITHUB_TOKENandGH_TOKENon shell startup, while preserving any token explicitly injected into the container environment. Interactive shells (.bashrc,.profile) and non-interactive shells (/etc/bash.envviaBASH_ENV) resolve the token dynamically instead of storing the literal value. - Available everywhere —
ghandcopilotare on$PATHfor both interactive shells (ddev ssh) and non-interactive commands (ddev exec)
GitHub CLI stores your token in the OS keychain by default — macOS Keychain,
GNOME Keyring (Secret Service) on Linux, Windows Credential Manager. It writes a
plaintext oauth_token: into ~/.config/gh/hosts.yml only as a silent fallback
when no keychain is reachable, such as on a headless server or WSL.
A container cannot reach the host keychain. So copying ~/.config/gh/ into the
container and running gh auth token there only ever works on hosts that took
the plaintext fallback. That is why forwarding used to succeed on headless Linux
and fail on macOS and Linux desktops, and why selecting an account with
DDEV_ASSISTANT_COPILOT_GH_USER could not help: the token was not in the file at
all.
This add-on resolves the token on the host instead:
- A
pre-starthook runsgh auth tokenon the host, honouring the configured hostname and user, and writes the result to~/.ddev-assistant-copilot/<project>/tokenwith mode600. - That directory is bind-mounted read-only into the container at
~/.cred-seed/auth/. - Shell startup resolves a token in this order:
COPILOT_GITHUB_TOKENalready present in the container environment- the host-resolved token from the seed mount
- in-container
gh auth token— still correct for hosts that legitimately use plaintext storage
The token is refreshed on every ddev start, so a rotated or revoked token is
picked up by a restart.
The token is deliberately kept out of DDEV's environment plumbing:
- Not in
web_environment, so it never appears indocker inspect - Not in
.ddev/.env.webor any file inside the project directory, so it cannot be committed - Not written literally into
/etc/bash.env,.bashrc, or.profile - Stored only in a mode-
600file under$HOME, outside the project
Note that inside the web container, PHP runs as the same user as copilot, so
anything reachable by Copilot CLI is reachable by your application code. The
practical benefit over an environment variable is that a file is not dumped by
phpinfo(), Xdebug stack traces, or a Symfony/Whoops error page. If that
residual exposure matters for your project, use ephemeral mode below.
To keep no token at rest at all, set this in
.ddev/config.assistant-copilot.yaml:
web_environment:
- DDEV_ASSISTANT_COPILOT_EPHEMERAL_TOKEN=trueddev start then stages no token. Instead, run Copilot through the host command:
ddev copilotThis resolves a fresh token from the host keychain for that invocation and
removes it when the session exits. The trade-off is that plain copilot inside
ddev ssh is left unauthenticated and will prompt you to log in.
ddev copilot works in the default mode too, where it simply refreshes the token
before starting the session.
The add-on ships with this non-secret default:
web_environment:
- DDEV_ASSISTANT_COPILOT_GH_HOSTNAME=github.com
# - DDEV_ASSISTANT_COPILOT_GH_USER=your-github-usernameUse this when you have multiple GitHub accounts, such as a personal account and
a work account, authenticated for the same host. Uncomment and set
DDEV_ASSISTANT_COPILOT_GH_USER in your installed
.ddev/config.assistant-copilot.yaml to tell the add-on which account should
provide COPILOT_GITHUB_TOKEN.
If either variable is empty or removed, the add-on does not pass that option to
gh auth token, allowing GitHub CLI's default account/host selection to apply.
# Run Copilot CLI with a token resolved fresh from the host keychain
ddev copilot
# Open a shell with Copilot CLI available
ddev ssh
copilot
# Run Copilot CLI non-interactively
ddev exec copilot --version
# GitHub CLI is also available, and authenticated
ddev exec gh auth statusYou can install Copilot CLI and gh inside a DDEV container yourself. This add-on automates the parts that are easy to get wrong or forget:
| This add-on | Manual setup | |
|---|---|---|
| GitHub CLI | Installed in the image layer via official apt repo; on $PATH for every shell |
Must install and re-install after image rebuilds |
| Copilot CLI | npm install -g @github/copilot on every start into ~/.local/bin |
Must run npm install manually; easy to lose on restart |
| Config approach | Seeds writable container ~/.config/gh/ and ~/.copilot/ from your host config on restart — zero setup if you already use gh and Copilot on the host |
Must copy or symlink config by hand; stale container files persist |
| Authentication | Token resolved on the host, so it works whether or not gh used the OS keychain; exported as COPILOT_GITHUB_TOKEN and GH_TOKEN without writing literal tokens into shell startup files, web_environment, or the project directory |
Copying ~/.config/gh only works on hosts without a keychain; otherwise you must export a token manually in every shell type |
| Non-interactive shells | BASH_ENV=/etc/bash.env ensures ddev exec gets PATH and token |
ddev exec often misses PATH and env vars |
| Mount safety | Pre-start hook ensures host config directories exist before Docker bind-mounts them | Bind-mount fails silently or blocks start if dirs are missing |
| Tests / CI | BATS integration tests, GitHub Actions CI matrix (DDEV stable + HEAD), daily scheduled runs | No automated verification |
This add-on does one thing: install GitHub Copilot CLI into your DDEV container and share your existing host configuration. Nothing else.
Contributed and maintained by @e0ipso