# How to Deploy the GitHub MCP Server Using Docker: Complete Setup Guide

> Easily deploy the GitHub MCP server with Docker. Follow our complete guide to set up the HTTP server in minutes using a simple Docker command. Get started now.

- Repository: [GitHub/github-mcp-server](https://github.com/github/github-mcp-server)
- Tags: how-to-guide
- Published: 2026-02-19

---

**Deploy the GitHub MCP server by running `docker run -i --rm -e GITHUB_PERSONAL_ACCESS_TOKEN=YOUR_PAT -p 8082:8082 ghcr.io/github/github-mcp-server http --port 8082`, which pulls the distroless image and starts the HTTP server on port 8082.**

The GitHub MCP (Model Context Protocol) server enables AI assistants to interact with GitHub repositories through a standardized interface. Deploying via Docker provides an isolated, reproducible environment using the official multi-stage image hosted at `ghcr.io/github/github-mcp-server`. This guide covers the complete deployment process based on the actual source code structure from the `github/github-mcp-server` repository.

## Docker Architecture and Build Process

The official **Dockerfile** implements a three-stage build that compiles both UI assets and the Go binary before deploying to a minimal runtime. Understanding this architecture helps troubleshoot deployment issues and optimize your configuration.

### Multi-Stage Build Stages

1. **`ui-build` stage**: Uses `node:20-alpine` to build frontend assets with Vite, outputting to `pkg/github/ui_dist/`
2. **`build` stage**: Uses `golang:1.25.7-alpine` to compile the server binary with embedded version metadata via `ldflags`
3. **Runtime stage**: Deploys to `gcr.io/distroless/base-debian12`, containing only the compiled binary at `/server/github-mcp-server`

The build process embeds version information directly into the binary:

```dockerfile
CGO_ENABLED=0 go build \
    -ldflags="-s -w -X main.version=${VERSION} \
              -X main.commit=$(git rev-parse HEAD) \
              -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
    -o /bin/github-mcp-server ./cmd/github-mcp-server

```

Key source files governing Docker behavior include:
- **[`cmd/github-mcp-server/main.go`](https://github.com/github/github-mcp-server/blob/main/cmd/github-mcp-server/main.go)**: Parses environment variables via Viper and routes to `stdio` or `http` subcommands
- **[`pkg/http/server.go`](https://github.com/github/github-mcp-server/blob/main/pkg/http/server.go)**: Implements the HTTP listener on `0.0.0.0:8082`
- **`Dockerfile`**: Defines the distroless runtime and exposes port 8082

## Prerequisites

Before deploying the GitHub MCP server using Docker, ensure you have:

- **Docker** installed and running on your host system
- A **GitHub Personal Access Token** with at least the `repo` scope (additional scopes required for specific toolsets like security or actions)
- (Optional) Familiarity with **MCP toolsets** to customize which GitHub features are exposed

## Deployment Methods

The container supports two primary execution modes controlled by subcommands in [`cmd/github-mcp-server/main.go`](https://github.com/github/github-mcp-server/blob/main/cmd/github-mcp-server/main.go).

### HTTP Mode (Production)

For network-accessible deployments, use the `http` subcommand to expose the server on port 8082:

```bash
docker run -i --rm \
  -e GITHUB_PERSONAL_ACCESS_TOKEN=YOUR_PAT \
  -p 8082:8082 \
  ghcr.io/github/github-mcp-server http \
  --port 8082

```

**Command breakdown:**
- `-i` keeps STDIN open for JSON-RPC communication
- `--rm` removes the container after stopping
- `-e GITHUB_PERSONAL_ACCESS_TOKEN` passes authentication to the server (read via `viper.GetString("personal_access_token")`)
- `-p 8082:8082` maps the internal port to your host
- `http` invokes the HTTP server defined in [`pkg/http/server.go`](https://github.com/github/github-mcp-server/blob/main/pkg/http/server.go)

### stdio Mode (Local Development)

For local testing or integration with hosts expecting standard I/O communication:

```bash
docker run -i --rm \
  -e GITHUB_PERSONAL_ACCESS_TOKEN=YOUR_PAT \
  ghcr.io/github/github-mcp-server stdio

```

Since `CMD ["stdio"]` is the default entrypoint in the Dockerfile, you can omit the subcommand:

```bash
docker run -i --rm \
  -e GITHUB_PERSONAL_ACCESS_TOKEN=YOUR_PAT \
  ghcr.io/github/github-mcp-server

```

## Configuration and Environment Variables

The server uses **Viper** for configuration management, mapping environment variables to command flags. Set these via `-e` flags in your docker run commands:

| Environment Variable | Purpose | Source Reference |
|---------------------|---------|------------------|
| `GITHUB_PERSONAL_ACCESS_TOKEN` | Authentication for GitHub API calls | [`main.go`](https://github.com/github/github-mcp-server/blob/main/main.go) (initConfig) |
| `GITHUB_TOOLSETS` | Comma-separated list of enabled tools (default: `context,repos,issues,pull_requests,users`) | [`main.go`](https://github.com/github/github-mcp-server/blob/main/main.go#L70-L77) |
| `GITHUB_READ_ONLY` | Boolean to disable write operations | Flag parsing in main.go |
| `GITHUB_DYNAMIC_TOOLSETS` | Enables runtime toolset discovery tools | Tool configuration |

Example with custom toolsets and read-only mode:

```bash
docker run -i --rm \
  -e GITHUB_PERSONAL_ACCESS_TOKEN=YOUR_PAT \
  -e GITHUB_TOOLSETS="repos,issues,actions,code_security" \
  -e GITHUB_READ_ONLY=true \
  ghcr.io/github/github-mcp-server http

```

## Docker Compose Configuration

For persistent deployments, use this [`docker-compose.yml`](https://github.com/github/github-mcp-server/blob/main/docker-compose.yml) structure:

```yaml
version: "3.9"
services:
  github-mcp-server:
    image: ghcr.io/github/github-mcp-server
    container_name: github-mcp-server
    restart: unless-stopped
    environment:
      GITHUB_PERSONAL_ACCESS_TOKEN: ${GITHUB_PAT}
      GITHUB_TOOLSETS: "repos,issues,pull_requests"
      GITHUB_DYNAMIC_TOOLSETS: "true"
    ports:
      - "8082:8082"
    command: http --port 8082

```

Start with: `docker-compose up -d`

## Verifying Your Deployment

Confirm the server is running correctly by checking the embedded version metadata:

```bash
docker run -i --rm \
  -e GITHUB_PERSONAL_ACCESS_TOKEN=YOUR_PAT \
  ghcr.io/github/github-mcp-server --version

```

Expected output includes the version, commit SHA, and build date injected during the Docker build process:

```

Version: dev
Commit: a1b2c3d4...
Build Date: 2024-07-30T12:34:56Z

```

For HTTP mode, verify connectivity by checking port 8082 responds to MCP protocol requests.

## Summary

- **The GitHub MCP server** is distributed as a multi-stage Docker image at `ghcr.io/github/github-mcp-server` using a distroless runtime for security
- **Authentication requires** setting the `GITHUB_PERSONAL_ACCESS_TOKEN` environment variable parsed by Viper in [`cmd/github-mcp-server/main.go`](https://github.com/github/github-mcp-server/blob/main/cmd/github-mcp-server/main.go)
- **Two execution modes** exist: `stdio` for local JSON-RPC over standard I/O (default), and `http` for network-accessible deployments on port 8082
- **Configuration happens** via environment variables that map to Cobra flags, including toolset selection and read-only restrictions
- **The Dockerfile** embeds build metadata via Go ldflags and deploys to `gcr.io/distroless/base-debian12` to minimize attack surface

## Frequently Asked Questions

### What is the difference between stdio and http modes in the GitHub MCP server?

**stdio mode** runs the server over standard input/output using JSON-RPC, which is ideal for local development and integration with VS Code or other editors that spawn the container as a subprocess. **http mode** exposes the server as a web service on port 8082 (defined in [`pkg/http/server.go`](https://github.com/github/github-mcp-server/blob/main/pkg/http/server.go)), making it suitable for remote deployments or microservice architectures where the MCP client connects over TCP/IP.

### How do I securely handle the GitHub Personal Access Token in Docker?

Never hardcode your PAT in docker run commands stored in shell history. Instead, use Docker secrets, environment files, or your shell's variable substitution (e.g., `-e GITHUB_PERSONAL_ACCESS_TOKEN=$GITHUB_PAT`). For Docker Compose, reference the variable in your `.env` file and ensure the file has restricted permissions (0600). The token is read by the Viper configuration in [`cmd/github-mcp-server/main.go`](https://github.com/github/github-mcp-server/blob/main/cmd/github-mcp-server/main.go) and passed to the GitHub API client.

### Can I customize which GitHub tools are available in the container?

Yes. Set the `GITHUB_TOOLSETS` environment variable to a comma-separated list of enabled categories. Available toolsets include `context`, `repos`, `issues`, `pull_requests`, `users`, `actions`, and `code_security`. This configuration is parsed in the `initConfig` function of [`main.go`](https://github.com/github/github-mcp-server/blob/main/main.go) and controls which handlers are registered in the server. You can also enable `GITHUB_DYNAMIC_TOOLSETS=true` to expose helper tools that list available capabilities at runtime.

### Why does the Docker image use a distroless base?

The **distroless** image (`gcr.io/distroless/base-debian12`) contains only the Go runtime and the compiled `github-mcp-server` binary, removing package managers, shells, and unnecessary system libraries. This significantly reduces the attack surface and image size compared to Alpine or Debian bases. As defined in the Dockerfile, the final stage copies only the binary to `/server/github-mcp-server` and sets it as the entrypoint, ensuring the container runs with minimal privileges.