← all posts

Running GitHub Copilot CLI in Docker with copilot_here

Gordon Beeming
Gordon Beeming
On this page8 sections ▾

I wanted to use GitHub Copilot CLI's command execution, including --allow-all-tools, while limiting which files it could access. I built copilot_here to run it in Docker with the current project mounted into the container.

That still lets the agent change or delete files in the project. It also has network access, so the container's mounts and network settings matter when deciding which permissions to give it.

#What I wanted from the setup

I had four goals for what this setup needed to do:

  • Project file access: Mount the current project without exposing my other host directories.
  • Auto-authentication: It should use my existing gh login without any extra steps.
  • Portability: No global Node.js or npm packages on my host machine.
  • Automatic execution: Keep the file-access boundary clear enough that I can decide when to allow commands without individual prompts.

#What the container isolates

Copilot runs inside the container with the current project mapped to /work. A command such as rm -rf . run there can delete the project's files on the host. Other host directories aren't exposed through that project mount.

This setup also passes authentication into the container. File isolation doesn't restrict what those credentials or network connections can access. In the setup described here, the container shares the host network. The project mount doesn't filter outgoing connections or prevent access to reachable local services.

That isolation is what gives me enough comfort to actually use --allow-all-tools. The risk isn't gone, but it's contained to a level I can live with.

The final working Copilot CLI session, showing the banner and the logged-in user.
Copilot CLI running in the container with authentication available.

#The final setup: the code

The complete solution is hosted on GitHub at https://github.com/GordonBeeming/copilot_here. The setup consists of two files: a Dockerfile to build the environment and an entrypoint.sh script to handle user permissions.

Here's the Dockerfile:

Dockerfile
# Use a slim Node.js base image, which gives us `npm`.
FROM node:20-slim

# Set non-interactive frontend to avoid prompts during package installation.
ENV DEBIAN_FRONTEND=noninteractive

# Install git, curl, gpg, and gosu for the entrypoint script.
RUN apt-get update && apt-get install -y \
  curl \
  gpg \
  git \
  gosu \
  && rm -rf /var/lib/apt/lists/*

# ARG for the Copilot CLI version - passed from build process
# This ensures cache invalidation when a new version is available
ARG COPILOT_VERSION=latest

# Install the standalone GitHub Copilot CLI via npm.
RUN npm install -g @github/copilot@${COPILOT_VERSION}

# Set the working directory for the container.
WORKDIR /work

# Copy the entrypoint script into the container and make it executable.
COPY entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/entrypoint.sh

# Label for image cleanup
LABEL project="copilot_here"

# The entrypoint script will handle user creation and command execution.
ENTRYPOINT [ "entrypoint.sh" ]

# The default command to run if none is provided.
CMD [ "copilot", "--banner" ]

And the entrypoint.sh script:

entrypoint.sh
#!/bin/bash
set -e

# Get the user and group IDs from environment variables, default to 1000 if not set.
USER_ID=${PUID:-1000}
GROUP_ID=${PGID:-1000}

# Create a group and user with the specified IDs.
groupadd --gid $GROUP_ID appuser_group >/dev/null 2>&1 || true
useradd --uid $USER_ID --gid $GROUP_ID --shell /bin/bash --create-home appuser >/dev/null 2>&1 || true

# Verify the user was created successfully
if ! id appuser >/dev/null 2>&1; then
    echo "Warning: Failed to create appuser, running as root" >&2
    mkdir -p /home/appuser/.copilot
    exec "$@"
fi

# Set up the .copilot directory and ensure ownership of the entire home dir.
mkdir -p /home/appuser/.copilot
chown -R $USER_ID:$GROUP_ID /home/appuser

# Switch to the new user and execute the command passed to the script.
exec gosu appuser "$@"

#Understanding the modes and features

The setup has two execution modes and a bunch of image variants.

#Execution modes

Safe mode (copilot_here) - Always asks for confirmation before executing commands. Good for everyday work where you want to stay in control.

YOLO mode (copilot_yolo) - Auto-approves all tool usage without prompting. Useful for trusted workflows, but be aware it will run commands without asking.

#Image variants

All functions support switching between Docker image variants using flags:

  • No flag - Base image (Node.js, Git, basic tools)
  • --dotnet (-d) - .NET image (includes .NET 8, 9 & 10 SDKs)
  • --dotnet8 (-d8) - .NET 8 image (includes .NET 8 SDK)
  • --dotnet9 (-d9) - .NET 9 image (includes .NET 9 SDK)
  • --dotnet10 (-d10) - .NET 10 image (includes .NET 10 SDK)
  • --playwright (-pw) - Playwright image (includes browser automation)
  • --dotnet-playwright (-dp) - .NET + Playwright image (includes browser automation)
  • --rust (-rs) - Rust image (includes Rust toolchain)
  • --dotnet-rust (-dr) - .NET + Rust image

#Additional options

  • -h or --help - Show usage help and examples (Bash/Zsh) or -h / -Help (PowerShell)
  • --no-cleanup - Skip cleanup of unused Docker images (Bash/Zsh) or -NoCleanup (PowerShell)
  • --no-pull - Skip pulling the latest image (Bash/Zsh) or -NoPull (PowerShell)

Both modes check your GitHub token scopes and warn if the token has more permissions than needed. They also clean up unused Docker images tagged with the project label automatically.

#Setup instructions

Both platforms have a one-liner installer.

For Linux/macOS (Bash/Zsh):

Terminal
curl -fsSL https://github.com/GordonBeeming/copilot_here/releases/download/cli-latest/install.sh | $SHELL

For Windows (PowerShell):

PowerShell
iex ([System.Text.Encoding]::UTF8.GetString((iwr -UseBasicParsing 'https://github.com/GordonBeeming/copilot_here/releases/download/cli-latest/install.ps1').Content))

This downloads the script and configures your PowerShell profile.

#Keeping up-to-date

To update to the latest version:

Terminal
# Linux/macOS or Windows PowerShell
copilot_here --update

This downloads the latest CLI binary, shows you what version changed, and reloads the updated functions in your current shell.

#Manual installation

For manual installation or a full list of options, visit the GitHub repository. The repo has docs on all flags, the image variants, Airlock network isolation config, and copy-paste install blocks if you prefer that approach.

#Usage

#Get help

Terminal
# Linux/macOS
copilot_here --help
copilot_yolo --help

# Windows
copilot_here -Help
copilot_yolo -Help

#Interactive mode

Start a full chat session with the welcome banner:

Terminal
# Base image (default)
copilot_here

# With .NET image
copilot_here -d

# With .NET + Playwright image
copilot_here -dp

#Non-interactive mode

Pass a prompt directly for a quick response.

Safe mode (asks for confirmation before executing):

Terminal
# Base image
copilot_here "suggest a git command to view the last 5 commits"

# .NET image
copilot_here -d "build and test this .NET project"

# .NET + Playwright image
copilot_here -dp "run playwright tests for this app"

# Fast mode (skip cleanup and pull)
copilot_here --no-cleanup --no-pull "quick question"

YOLO mode (auto-approves execution):

Terminal
# Base image
copilot_yolo "write a function that reverses a string"

# .NET image
copilot_yolo -d "create a new ASP.NET Core API project"

# .NET + Playwright image
copilot_yolo -dp "write playwright tests for the login page"

# Fast mode (skip cleanup)
copilot_yolo --no-cleanup "generate README"

#Choosing an image

As the project grew, I kept running into cases where the base image wasn't enough. .NET projects need SDKs. Browser tests need Chromium. So I added specialized variants on top of the base image.

Base image (latest): Node.js 20, Git, and basic tools. Works for general scripting and anything that doesn't need a specific runtime.

.NET image (dotnet): The original image included .NET 8.0 and 9.0 SDKs with ASP.NET Core runtimes and ICU libraries. The flag list above includes the later .NET 10 variants too.

.NET + Playwright image (dotnet-playwright): Everything in the .NET image, plus Playwright 1.56.0 and Chromium with all its dependencies. About 500-600MB larger than the base image because of the Chromium binaries, so only use it when you need it.

You switch between variants with flags like -d for .NET or -dp for .NET + Playwright, rather than manually editing image names.

#Automatic cleanup

The original cleanup removed unused copilot_here images filtered by the project=copilot_here label. The October update changed this to retain recent images and avoid downloading the same image on every launch.

If you want to skip the cleanup for faster startup, use --no-cleanup (or -NoCleanup on PowerShell). You can also skip pulling the latest image with --no-pull (or -NoPull) if you need to move fast and already have the image locally.

#How I use it

I keep copilot_here for normal use and copilot_yolo for projects where I'm comfortable allowing automatic execution. I choose -d for .NET work and -dp when I also need browser tests. The project directory and available credentials still need the same care as any other tool that can modify files and run commands.

The GitHub repository has the source and current installation details.

Gordon Beeming
Gordon Beeming

Father • Husband • Triathlete • SSW Solution Architect

Related posts