Skip to main content

Docker: Test, Develop, and Deploy

Use Docker for repeatable testing, frontend development without Go, production deployment, and automated skill validation in CI.

Mode Selection Diagram

Command mapping:

Commandmisemake
Test (offline)mise run test:dockermake test-docker
Test (online)mise run test:docker:onlinemake test-docker-online
Playground (start + shell)mise run playgroundmake playground
Playground (stop)mise run playground:downmake playground-down
Sandbox (advanced)./scripts/sandbox.sh <up|down|shell|reset|status|logs|bare>
Dev API servermise run dev:dockermake dev-docker
Dev stopmise run dev:docker:downmake dev-docker-down
Docker buildmise run docker:buildmake docker-build
Docker multiarchmise run docker:build:multiarchmake docker-build-multiarch

What You Can Use It For

ModeBest forNetworkLifecycle
Offline test sandboxStable regression checks (build + unit + integration)DisabledOne-shot
Online test sandboxOptional remote install/update checksEnabledOne-shot
Interactive playgroundManual command exploration and demosEnabledPersistent
Dev profileGo API server in Docker + Vite HMR on hostEnabledPersistent
DevcontainerVS Code / Codespaces one-click dev environmentEnabledPersistent
Production imageLightweight deployment (docker/production/)EnabledPersistent
CI imageSkill validation in pipelines (docker/ci/)EnabledOne-shot

Common Scenarios

1. Verify local install/update logic deterministically

Use this when you are changing install / update behavior and want a CI-like local gate.

mise run test:docker
make test-docker

This validates local-path and file:// workflows in isolation.

2. Run optional remote-source checks

Use this for GitHub/remote-source validation that depends on network access.

make test-docker-online

3. Open a dedicated playground and explore all commands

One command to start and enter the playground:

make playground
mise run playground

Inside the playground, skillshare and ss are ready. Both global mode and project mode are pre-initialized:

skillshare --help
ss status
skillshare list

Project Mode in the Playground

The playground automatically sets up a demo project at ~/demo-project with a sample skill and a claude target. You can start exploring project mode right away:

cd ~/demo-project
skillshare status # auto-detects project mode
skillshare list
skillshare sync --dry-run

To launch the web dashboard, use the built-in aliases:

skillshare-ui            # global mode dashboard → http://localhost:19420
skillshare-ui-p # project mode dashboard (~/demo-project) → http://localhost:19420

Then open http://localhost:19420 on your host machine (port is mapped via Docker Compose).

The playground automatically picks up your GitHub token from the host for skillshare search. It checks in order: $GITHUB_TOKEN$GH_TOKENgh auth token. No extra setup needed if you're already authenticated on the host.

# If not detected, set it before starting the playground:
export GITHUB_TOKEN=ghp_your_token_here
make playground

When finished:

make playground-down

Use Cases by Role

Individual developers

ScenarioWhat to useWhat it replaces
Try skillshare without installing Go/Nodedocker run ghcr.io/runkids/skillshareInstall Go + Node + pnpm, then build from source
Run full test suite before opening a PRmake test-dockerDepend on local toolchain (Go version mismatch = flaky results)
Frontend work without Go installedmake dev-docker + cd ui && pnpm run devMust install Go 1.25+ locally to run the API server
Demo skillshare to a colleaguemake playground → Web UI on :19420Walk them through a full local install
Verify Linux behavior on Apple Siliconmake docker-buildPush to CI and wait

Teams and open-source contributors

ScenarioWhat to useWhat it solves
New contributor onboardingmake playground — one command, ready to goNo more "install Go, set PATH, clone, build" setup guide
Automated skill quality gate in CIdocker run ghcr.io/.../skillshare-ci audit /skillsPreviously required installing Go + building from source in every workflow
"Works on my machine" across contributorsDocker pins Go 1.25.5 + all dependenciesDifferent local Go versions causing test flakes
PR reviewer reproducing an issue./scripts/test_docker.sh --cmd "go test -run TestXxx ..."Must clone + full local setup to reproduce

Enterprise and self-hosted deployment

ScenarioWhat to useValue
Internal skill management dashboardProduction image + volume mount for skillsOne container, no Go/Node on the server
Kubernetes deploymentProduction image (healthcheck + graceful shutdown + non-root)Ready for readiness/liveness probes, passes PodSecurityPolicy
Automated skill PR reviewCI image + skillshare audit in GitHub ActionsBlock unsafe skills from merging — one line in your workflow
Container security complianceread_only + cap_drop: ALL + no-new-privilegesPasses CIS Docker Benchmark, Trivy, and Aqua scans
ARM servers (AWS Graviton) for cost savingsmake docker-build-multiarchNative arm64 image, no emulation overhead

Quick examples

Self-hosted dashboard with persistent skills:

docker run -d \
-p 19420:19420 \
-v skillshare-data:/home/skillshare/.config/skillshare \
ghcr.io/runkids/skillshare

CI skill audit in GitHub Actions:

- name: Audit skills
run: |
docker run --rm \
-v ${{ github.workspace }}/skills:/skills \
ghcr.io/runkids/skillshare-ci audit /skills

Kubernetes deployment (minimal):

apiVersion: apps/v1
kind: Deployment
metadata:
name: skillshare
spec:
replicas: 1
template:
spec:
containers:
- name: skillshare
image: ghcr.io/runkids/skillshare:latest
ports:
- containerPort: 19420
livenessProbe:
httpGet:
path: /api/health
port: 19420
readinessProbe:
httpGet:
path: /api/health
port: 19420
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true

Dev Profile

Two ways to develop the frontend with Vite HMR:

With Go installed locally (single command):

make ui-dev              # starts Go API server + Vite dev server together
# Open http://localhost:5173

Without Go (Go API runs in Docker, auto-rebuilds on Go changes):

# Terminal 1
make dev-docker # Go API in Docker + Compose Watch (localhost:19420)

# Terminal 2
cd ui && pnpm run dev # Vite dev server (localhost:5173, proxies /api → :19420)

# When done
make dev-docker-down

Both approaches give you instant HMR for ui/ changes. The Docker variant pins the Go toolchain so backend behavior is consistent across contributors. When you edit Go files, Compose Watch detects the change, rebuilds the container, and restarts the API server automatically. Requires Docker Compose v2.22+.

Note: Go code changes require restarting the server when using make ui-dev (Ctrl+C and re-run). make dev-docker handles this automatically via Compose Watch.


Devcontainer (VS Code / Codespaces)

Open the project in a ready-to-code container — no local Go, Node, or pnpm needed.

Devcontainer vs Playground

Both use the same base image and demo content. The playground (make playground) is a terminal-only environment for exploring commands. The devcontainer adds VS Code integration (IntelliSense, debugger, extensions) for developing the skillshare codebase itself.

Prerequisites

GitHub Codespaces

On GitHub, click Code → Codespaces → New codespace. The devcontainer config is picked up automatically — no local Docker or extension needed.

Getting started

  1. Open the project folder in VS Code
  2. Press Ctrl+Shift+P (or Cmd+Shift+P on macOS) and select Dev Containers: Reopen in Container
  3. Wait for the container to build (first time takes a few minutes, subsequent opens are fast)
  4. Once ready, the setup script builds the binary and creates demo skills automatically

What's included

The devcontainer reuses the same docker/sandbox/Dockerfile as the sandbox, so you get:

  • Go 1.25 toolchain
  • Node.js 24 + pnpm (via devcontainer feature) — enables make ui-dev and cd website && pnpm start inside the container
  • VS Code extensions: Go, Tailwind CSS, ESLint, Prettier
  • Ports forwarded: 19420 (Web UI), 5173 (Vite HMR), 3000 (Docusaurus)
  • Source code mounted at /workspace
  • Pre-configured demo environment — same as the interactive playground:
    • Shortcut commands in PATH (ss, ui, docs)
    • Frontend dependencies pre-installed (ui/ and website/)
    • Global demo skills (audit examples, deploy checklist)
    • Custom audit rules (global + project)
    • Demo project at ~/demo-project with project-mode skills

Quick start after container opens

ss status                 # global mode — already initialized
ss list # see demo skills (flat + nested)
ss audit # run audit with custom rules

cd ~/demo-project
ss status # auto-detects project mode
ss audit # project-level audit
ui -p # switch API to project mode → http://localhost:5173

Frontend development

PortServiceCommand
5173Vite (React UI + HMR)ui or ui -p
19420Go API backendstarted by ui / ui -p
3000Docusaurusdocs
ui                        # global mode: API + Vite → http://localhost:5173
ui -p # project mode: API + Vite → http://localhost:5173
ui stop # stop API + Vite
docs # documentation site → http://localhost:3000
docs stop # stop Docusaurus

ui starts both the Go API backend (port 19420, background) and Vite dev server (port 5173, HMR). Switching between ui and ui -p automatically restarts the API in the new mode. VS Code auto-forwards ports to your host browser.

Token configuration

Tokens for private repo access (GITHUB_TOKEN, GITLAB_TOKEN, etc.) can come from multiple sources. They are checked in this order:

PrioritySourceSetup
1.devcontainer/.envCopy .env.example.env, fill in values (gitignored)
2Host env varsSet in ~/.zshrc — forwarded via remoteEnv in devcontainer.json
3gh auth loginGITHUB_TOKEN auto-detected on container start (GitHub only)

All sources are optional. You can also use export manually inside the container at any time.

Check current state:

credential-helper status

Private repo testing

VS Code Dev Containers automatically forwards your host git credentials into the container. This means git clone of private repos may succeed even without explicit token env vars — the forwarded credential helper handles auth silently.

To disable all authentication (credential helper + token env vars) for testing:

eval "$(credential-helper --eval off)"    # disable everything
eval "$(credential-helper --eval on)" # restore everything
credential-helper status # check current state

Without --eval, only the git credential helper is toggled (token env vars remain active).

Running tests

make test          # unit + integration
make test-unit # unit only
make lint # go vet

Production and CI Images

Image comparison

Three Dockerfiles serve different purposes:

ProductionCISandbox
Imageghcr.io/runkids/skillshareghcr.io/runkids/skillshare-ciLocal build only
Dockerfiledocker/production/Dockerfiledocker/ci/Dockerfiledocker/sandbox/Dockerfile
Basedebian:bookworm-slimdebian:bookworm-slimgolang:1.25.5-bookworm
Includesgit, curl, tinigit onlyGo toolchain, gh, jq, air, delve, pre-built UI
Non-rootYes (UID 10001)NoNo
PID 1tinidefaultdefault
HealthcheckYes (/api/health)NoNo
Entrypointskillshare ui (Web dashboard)skillshare (direct CLI)entrypoint.sh (test runner)
Use caseSelf-hosted dashboard, KubernetesCI/CD skill validationDevelopment, testing, playground
Published to GHCRYesYesNo
Multi-archamd64 + arm64amd64 + arm64Host arch only

When to use which:

  • Production — Deploy the Web UI dashboard on a server or Kubernetes cluster
  • CI — Run audit, install --dry-run, or other validation commands in GitHub Actions / GitLab CI
  • Sandbox — Local development (make test-docker, make playground, make dev-docker)

Production image

Build a lightweight production image with the embedded Web UI:

make docker-build                          # current platform only (fast, for local testing)
make docker-build-multiarch # linux/amd64 + linux/arm64 (slow, for registry push)

docker-build only produces an image for your machine's architecture — an arm64 image from Apple Silicon won't run on x86 servers. Use docker-build-multiarch when pushing to a registry so any platform gets the right image automatically.

The production image uses tini as PID 1, runs as a non-root user (UID 10001), includes a healthcheck, and auto-initialises config on first run. Default command: skillshare ui -g --host 0.0.0.0 --no-open.

Published images are available on GHCR (pushed automatically on tag):

# Pull and run (auto-selects amd64 or arm64)
docker run -d -p 19420:19420 ghcr.io/runkids/skillshare

# With persistent skill data
docker run -d -p 19420:19420 \
-v skillshare-data:/home/skillshare/.config/skillshare \
ghcr.io/runkids/skillshare

CI image

A minimal image for validating skills in CI pipelines:

docker build -f docker/ci/Dockerfile -t skillshare-ci .
docker run --rm -v ./my-skills:/skills skillshare-ci audit /skills

The CI image's entrypoint is skillshare itself, so you pass subcommands directly:

# Audit with threshold
docker run --rm -v ./skills:/skills ghcr.io/runkids/skillshare-ci audit /skills --threshold HIGH

# Dry-run install to verify a repo
docker run --rm ghcr.io/runkids/skillshare-ci install org/repo --dry-run

Sandbox image

The sandbox image is for local development and testing only (not published to GHCR). It includes the full Go toolchain, development tools (air, delve), GitHub CLI, and pre-built frontend assets.

Used by: make test-docker, make test-docker-online, make playground, make dev-docker.

See the Playground and Dev Profile sections above for usage.

Image tags and versioning

On tag push (v*), the docker-publish GitHub Actions workflow builds and pushes both production and CI images to GHCR with multi-arch support.

Each image is tagged with three patterns:

Tag patternExampleDescription
v<major>.<minor>.<patch>v0.16.1Exact version (immutable)
<major>.<minor>0.16Latest patch for this minor version (rolling)
sha-<short>sha-153464aGit commit SHA (immutable)
tip

Use exact version tags (v0.16.1) in production for reproducibility. Use minor tags (0.16) to get patch updates automatically. Use sha- tags to pin to a specific commit.

Browse published versions at GitHub Packages.


Limits and Expectations

  • Playground and dev profile share port 19420 — run only one at a time. Stop the other first (make playground-down or make dev-docker-down).
  • Offline sandbox cannot validate network-dependent features (for example remote install from GitHub).
  • Playground uses container-local HOME, so it does not directly modify your real host home config.
  • Go code changes are picked up automatically (go build runs inside the container from mounted source). Frontend (ui/) changes are picked up instantly when running make ui-dev (Vite HMR) inside the devcontainer. For the playground, run make ui-build on the host first since the playground container does not include Node.js.
  • If you need custom experiments, pass commands directly:
./scripts/test_docker.sh --cmd "go test -v ./tests/integration/..."
./scripts/sandbox_playground_shell.sh "skillshare list"

See Also