# Container Deployment of Copilot SDK with Kubernetes and Persistent Storage: A Complete Guide

> Deploy the GitHub Copilot SDK on Kubernetes with persistent storage. Implement SessionFSProvider and use PVCs to ensure session data survives pod restarts. Get the complete guide.

- Repository: [GitHub/copilot-sdk](https://github.com/github/copilot-sdk)
- Tags: how-to-guide
- Published: 2026-08-02

---

**To deploy the GitHub Copilot SDK in Kubernetes with persistent storage, implement the `SessionFSProvider` interface to write session data to a mounted volume, then wire a PersistentVolumeClaim (PVC) to your container to ensure files and SQLite databases survive pod restarts.**

The GitHub Copilot SDK is a language-agnostic library that embeds AI-driven assistants into applications. When running container deployment of the Copilot SDK with Kubernetes and persistent storage, you need session data—such as files, tool result caches, and SQLite databases—to persist beyond the lifecycle of individual pods. This is achieved by implementing provider interfaces defined in the SDK and mounting a Kubernetes PVC to a stable filesystem path.

## Understanding the Persistence Architecture

The Copilot SDK delegates all storage operations to pluggable providers rather than handling persistence internally. This design allows the same codebase to run ephemerally in development and statefully in production Kubernetes clusters.

### The SessionFSProvider Interface

At the heart of the persistence mechanism is the `SessionFSProvider` interface defined in [`go/session_fs_provider.go`](https://github.com/github/copilot-sdk/blob/main/go/session_fs_provider.go). This contract requires implementations of standard filesystem operations:

- `ReadFile(path string) (string, error)`
- `WriteFile(path, content string, mode *int) error`
- `MakeDirectory(path string) error`
- `ReadDirectory(path string) ([]copilot.SessionFSDirectoryItem, error)`

When your application creates a `copilot.Client`, you inject a provider instance that translates these method calls into actual disk operations. According to the source code in [`session_fs_provider.go`](https://github.com/github/copilot-sdk/blob/main/session_fs_provider.go), the SDK uses this abstraction to remain agnostic about underlying storage mechanisms.

### Optional SQLite Support

For applications storing structured data like tool histories or vector embeddings, the SDK supports an optional `SessionFSSqliteProvider` interface—defined in the same [`session_fs_provider.go`](https://github.com/github/copilot-sdk/blob/main/session_fs_provider.go) file. This extension adds:

- `SqliteExists() (bool, error)` – Checks for a database file at the configured root
- `SqliteQuery(qt rpc.SessionFSSqliteQueryType, query string, params map[string]any) (*copilot.SessionFSSqliteQueryResult, error)` – Executes SQLite operations

The provider determines the database location. When mounted to a PVC, the SQLite file survives pod termination and rescheduling.

## Building the Container Image

Start with a multi-stage Dockerfile that compiles the SDK binary and exposes a writable directory for the PVC mount. The following configuration uses Go 1.22 and creates a minimal Alpine-based image:

```dockerfile
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/copilot-sdk ./go/main.go

FROM alpine:3.19
RUN apk add --no-cache ca-certificates
COPY --from=builder /bin/copilot-sdk /usr/local/bin/copilot-sdk

# The SDK expects a writable directory at /workspace (mounted via PVC)

VOLUME /workspace
ENTRYPOINT ["/usr/local/bin/copilot-sdk"]

```

This image expects a writable `/workspace` directory where the provider will store files and SQLite databases.

## Implementing the Storage Provider

Create a `diskProvider` struct that implements both `SessionFSProvider` and `SessionFSSqliteProvider`, writing to a configurable root path matching your PVC mount point. Below is a minimal implementation:

```go
package main

import (
	"database/sql"
	"os"
	"path/filepath"

	"github.com/github/copilot-sdk/go"
	"github.com/github/copilot-sdk/go/rpc"
	_ "modernc.org/sqlite"
)

// diskProvider implements both SessionFSProvider and SessionFSSqliteProvider.
type diskProvider struct {
	root string // e.g., "/workspace"
}

// Helper to resolve absolute paths.
func (p *diskProvider) resolve(pth string) string { return filepath.Join(p.root, pth) }

func (p *diskProvider) ReadFile(path string) (string, error) {
	b, err := os.ReadFile(p.resolve(path))
	if err != nil {
		return "", err
	}
	return string(b), nil
}

func (p *diskProvider) WriteFile(path, content string, mode *int) error {
	full := p.resolve(path)
	if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
		return err
	}
	perm := os.FileMode(0o644)
	if mode != nil {
		perm = os.FileMode(*mode)
	}
	return os.WriteFile(full, []byte(content), perm)
}

// SQLite support – the DB lives at "<root>/session.db".
func (p *diskProvider) SqliteExists() (bool, error) {
	_, err := os.Stat(p.resolve("session.db"))
	if os.IsNotExist(err) {
		return false, nil
	}
	return err == nil, err
}

func (p *diskProvider) SqliteQuery(qt rpc.SessionFSSqliteQueryType, query string, params map[string]any) (*copilot.SessionFSSqliteQueryResult, error) {
	db, err := sql.Open("sqlite", p.resolve("session.db"))
	if err != nil {
		return nil, err
	}
	defer db.Close()
	
	// Execute query and marshal results into SessionFSSqliteQueryResult.
	// Reference session_fs_sqlite_e2e_test.go for full transaction handling.
	return nil, nil
}

```

This implementation mirrors the signatures found in [`session_fs_provider.go`](https://github.com/github/copilot-sdk/blob/main/session_fs_provider.go) and the SQLite transaction logic demonstrated in [`session_fs_sqlite_e2e_test.go`](https://github.com/github/copilot-sdk/blob/main/session_fs_sqlite_e2e_test.go).

## Wiring Everything Together

Initialize the Copilot client with your provider implementation, pointing the root to `/workspace` to align with the Kubernetes volume mount:

```go
package main

import (
	"context"
	"log"
	"time"

	"github.com/github/copilot-sdk/go"
)

func main() {
	provider := &diskProvider{root: "/workspace"}

	client, err := copilot.NewClient(
		copilot.WithSessionFSProvider(provider),
		copilot.WithSessionFSSqliteProvider(provider),
	)
	if err != nil {
		log.Fatalf("client init: %v", err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	
	sess, err := client.NewSession(ctx, copilot.SessionConfig{})
	if err != nil {
		log.Fatalf("session: %v", err)
	}
	
	// This file persists across pod restarts because it writes to the PVC-backed directory.
	if err := sess.WriteFile(ctx, "welcome.txt", "Hello from Copilot SDK!", nil); err != nil {
		log.Fatalf("write: %v", err)
	}
	log.Println("session started; file written to persistent volume")
}

```

When this binary runs inside a container, [`welcome.txt`](https://github.com/github/copilot-sdk/blob/main/welcome.txt) is written to [`/workspace/welcome.txt`](https://github.com/github/copilot-sdk/blob/main//workspace/welcome.txt) on the PVC-backed filesystem.

## Kubernetes Deployment Configuration

Deploy the application using a PersistentVolumeClaim for durability and a Deployment that mounts the volume at the expected path:

```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: copilot-sdk-pvc
spec:
  accessModes: [ReadWriteOnce]
  resources:
    requests:
      storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: copilot-sdk
spec:
  replicas: 1
  selector:
    matchLabels:
      app: copilot-sdk
  template:
    metadata:
      labels:
        app: copilot-sdk
    spec:
      containers:
        - name: sdk
          image: ghcr.io/github/copilot-sdk:latest
          volumeMounts:
            - name: workspace
              mountPath: /workspace
          env:
            - name: COPILOT_API_ENDPOINT
              value: "https://api.githubcopilot.com"
      volumes:
        - name: workspace
          persistentVolumeClaim:
            claimName: copilot-sdk-pvc

```

- The **PVC** guarantees that `/workspace` survives pod termination with `ReadWriteOnce` access mode suitable for single-replica deployments.
- The **Deployment** mounts the PVC at `/workspace`, matching the `diskProvider.root` configuration.
- When you update the container image (e.g., adding new tool definitions), existing session files and the SQLite database remain intact on the persistent volume.

## Summary

- **Implement `SessionFSProvider`** to give the Copilot SDK a filesystem interface that reads and writes to a local path.
- **Optionally implement `SessionFSSqliteProvider`** to enable SQLite persistence for structured session data like tool histories.
- **Mount a PersistentVolumeClaim** at `/workspace` (or your provider's root path) to ensure data survives pod restarts, rescheduling, and image updates.
- **Reference implementation files** include [`go/session_fs_provider.go`](https://github.com/github/copilot-sdk/blob/main/go/session_fs_provider.go) for interface definitions and [`go/internal/e2e/session_fs_sqlite_e2e_test.go`](https://github.com/github/copilot-sdk/blob/main/go/internal/e2e/session_fs_sqlite_e2e_test.go) for SQLite transaction patterns.
- **Scale safely** by maintaining the PVC separately from the Deployment, allowing you to update the SDK binary without losing user session data.

## Frequently Asked Questions

### How do I implement `SessionFSProvider` for the Copilot SDK?

You create a struct that implements methods like `ReadFile`, `WriteFile`, and `MakeDirectory`, then inject it via `copilot.WithSessionFSProvider()` when constructing the client. The interface is defined in [`go/session_fs_provider.go`](https://github.com/github/copilot-sdk/blob/main/go/session_fs_provider.go), and your implementation should handle path resolution relative to your PVC mount point.

### Does the Copilot SDK require SQLite for session persistence?

No, SQLite is optional. If your application only needs to store files (text, JSON, binary data), implementing `SessionFSProvider` alone is sufficient. You only need to implement `SessionFSSqliteProvider` if your tools require relational data storage or vector operations using SQLite, as demonstrated in the SDK's [`session_fs_sqlite_e2e_test.go`](https://github.com/github/copilot-sdk/blob/main/session_fs_sqlite_e2e_test.go) examples.

### What happens to session data when a pod restarts in Kubernetes?

If you have mounted a PersistentVolumeClaim at the provider's root path (e.g., `/workspace`), session data—including files written via `WriteFile` and SQLite databases—survives the restart because the PVC exists independently of the pod lifecycle. Without a PVC, the container filesystem is ephemeral and all session data is lost when the pod terminates.

### Can I scale the Copilot SDK Deployment to multiple replicas?

Caution is required when scaling beyond one replica. If using `ReadWriteOnce` PVC access mode (as shown in the example), the volume can only mount to one node at a time, limiting you to a single replica or requiring a pod distribution strategy. For multi-replica deployments, use `ReadWriteMany` access mode with a compatible storage class (like NFS or EFS), or switch to a stateless architecture where each replica manages its own ephemeral storage.