Back
Go
Vue 3
Vite
PostgreSQL
JWT
WebSockets
JavaScript
Docker
Actively developed
go-container
Self-hosted Docker admin panel, a simplified Portainer
Overview
A web panel for managing containers, images, networks, volumes, "stacks" (docker-compose projects) and role-based users, all from the browser. The backend acts as an authenticated proxy over the host's Docker Engine API, not a reimplementation of Docker.
It ships as a single Go binary that embeds the production frontend build, so in production it's one container serving both the API and the SPA.
Highlights
- Full management of containers, images, networks, volumes and stacks (docker-compose) from a Vue 3 SPA.
- Interactive shell over WebSocket inside each container (xterm.js) plus live log streaming.
- Role system (viewer / editor / administrator) that revalidates the session against the database on every request, so deactivating a user cuts off access instantly.
- Stacks aren't a plain Docker pass-through: they're persisted in Postgres and every operation (deploy, stop, pull, force-deploy) invokes the real docker compose CLI, retaining the record even if a step fails.
- A watcher listens for container death events and persists their last logs, so you can see why a container died even after it's removed.
- Crash recovery: if the process dies without a clean shutdown (panic, OOM-kill), the leftover log is recovered and stored on the next boot.
Architecture
- Go backend with Gin as the router and GORM + PostgreSQL for persistence.
- A hand-rolled client talks directly to the Docker socket over the Engine API, without the official SDK.
- Vue 3 (Composition API) + Vite + Pinia frontend, no CSS framework.
- JWT authentication; WebSockets (shell, logs) also accept the token as a query param since a native browser WebSocket can't send an Authorization header.
- Production build: a multi-stage Dockerfile compiles the frontend, embeds it into the Go binary (go:embed), and produces a final image that only needs the binary plus the docker compose CLI.
Quick start
docker-compose.yml (development)
services:
app:
build:
context: ./app
target: dev
ports:
- "9000:9000"
volumes:
- ./app:/app
- /app/tmp
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DB_HOST=db
- DB_PORT=5432
- DB_USER=postgres
- DB_PASSWORD=postgres
- DB_NAME=go_container
- DOCKER_SOCKET_PATH=/var/run/docker.sock
- CORS_ALLOWED_ORIGIN=http://localhost:5173
depends_on:
- db
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=go_container
ports:
- "5433:5432"
volumes:
- pgdata:/var/lib/postgresql/data
front:
build:
context: ./front
target: dev
ports:
- "5173:5173"
volumes:
- ./front:/app
- /app/node_modules
volumes:
pgdata:Start the environment
docker compose up -dEnvironment variables
| Variable | Default | Purpose |
|---|---|---|
| SECRET_KEY | default_secret_key | JWT signing secret — change it in production. |
| DB_HOST | localhost | PostgreSQL host. |
| DB_PORT | 5432 | PostgreSQL port. |
| DB_USER | postgres | PostgreSQL user. |
| DB_PASSWORD | postgres | PostgreSQL password. |
| DB_NAME | go_container | Database name. |
| DOCKER_SOCKET_PATH | /var/run/docker.sock | Docker Engine socket to proxy. |
| CORS_ALLOWED_ORIGIN | http://localhost:5173 | Single origin allowed by the CORS middleware. |
| STACKS_DIR | stacks | Directory where each stack's docker-compose.yml is materialized. |