Back
Actively developed

go-container

Self-hosted Docker admin panel, a simplified Portainer

Go Go Vue 3 Vue 3 Vite Vite PostgreSQL PostgreSQL JWT JWT WebSockets WebSockets JavaScript JavaScript Docker Docker

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 -d

Environment variables

VariableDefaultPurpose
SECRET_KEYdefault_secret_keyJWT signing secret — change it in production.
DB_HOSTlocalhostPostgreSQL host.
DB_PORT5432PostgreSQL port.
DB_USERpostgresPostgreSQL user.
DB_PASSWORDpostgresPostgreSQL password.
DB_NAMEgo_containerDatabase name.
DOCKER_SOCKET_PATH/var/run/docker.sockDocker Engine socket to proxy.
CORS_ALLOWED_ORIGINhttp://localhost:5173Single origin allowed by the CORS middleware.
STACKS_DIRstacksDirectory where each stack's docker-compose.yml is materialized.