open source Proxmox VE Apache 2.0

Your homelab hired a goblin.

GoblinDock turns "spin up a fully-configured VM" into a few clear buttons. Stack Lego-style blocks into a reusable template, hit deploy, and the goblin builds the VM fresh — cloud-init, Ansible, console and all.

One container · one SQLite file · no Redis, no Postgres, no moving parts.

The GoblinDock goblin operating a crane
VM deployed42s · agent reported IP
template savedAI Dev Box · 6 blocks
console livenoVNC + serial

One container. Everything inside.

Python FastAPI Uvicorn SQLite · WAL proxmoxer ansible-runner cloud-init paramiko React CodeMirror xterm.js noVNC Docker
The flow

Three steps from a raw cloud image to a running VM.

Add the raw material, shape it into a reusable template, then summon as many VMs as you like — each one built fresh and configured on the way up.

01 · RAW MATERIAL

Add a base image

Give GoblinDock a public cloud image URL — Ubuntu 24.04 LTS, for example. It downloads and imports it into Proxmox as the starting point every build is cut from.

ISO · base image
02 · BUILD

Build a template

Stack the blocks you want — packages, Docker, users, scripts — pick a node and a size, and save it as a reusable template like AI Dev Box or MySQL node. No baking, no throwaway VM.

template = image + blocks + size
03 · SUMMON

One-click deploy

Pick a template, name the VM, answer anything it asks for, and click deploy. GoblinDock builds it fresh from the base image, configures it, and reports the IP once the guest agent answers.

deploy · answer its inputs
In the toolbox

Everything a homelab needs to stop hand-building VMs.

Provisioning, configuration, lifecycle and a real console — bundled into one panel your whole household (or team) can share.

Lego-style blocks

44 built-in blocks plus your own — Tailscale, K3s, SSH hardening, Compose stacks, databases, AI tooling and more. Fork a built-in to make it yours.

A real graphical console

The same noVNC console Proxmox uses, plus an xterm.js serial tab — proxied so the browser only ever talks to GoblinDock.

Live jobs over SSE

Every long action is a job with a step checklist, progress bar and a streaming log. Watch the goblin work in real time.

Multi-user, properly isolated

First-run admin setup, Admin / User roles, per-user VM isolation, per-target resource ceilings and an audit log. A non-admin only ever sees their own VMs and a redacted view of everything else.

Snapshots, before you tinker

Take, roll back and delete Proxmox-native snapshots — optionally with RAM state — straight from the VM detail page. Snapshot before a risky change, roll back in seconds.

Full lifecycle, from the dashboard or the detail page

Start, stop, restart, rebuild or destroy — with live CPU / RAM / disk, full config, guest-agent OS & network info, and the deployment log all on one per-VM page. A rebuilt VM even keeps its static IP and VLAN.

The part people are curious about

Stack blocks. GoblinDock compiles them.

When you stack several blocks, GoblinDock doesn't run them one-by-one as separate executions — it merges them by phase. Same phase, one execution, in canvas order.

On the canvas — 4 blocks
Base OS Setup cloud-init
Install Packages ansible
Docker CE ansible
Run Script ansible
first boot — cloud-init runcmd 1 script · as root
# one script, runs as root
set -e
echo '>>> GoblinDock: Base OS Setup'
timedatectl set-timezone UTC
localectl set-locale LANG=en_US.UTF-8
post-boot — one ansible playbook 1 play · 3 tasks
- name: deploy-gd-vm
  hosts: all
  become: true
  tasks:
    - name: Install Packages   # task 1
    - name: Install Docker CE   # task 2
    - name: Run Script         # task 3

Three ansible blocks aren't three SSH sessions — they're three tasks in one playbook, run in order. See the exact generated YAML any time with View YAML in the builder.

Look inside the lair

A control room, not a config file.

A vendored React SPA — no build step — served straight from the container. Mono-forward, goblin-gold, dark by default.

The template builder

Drag blocks into sections. Watch the template take shape.

Compose a template from typed blocks — each tagged cloud-init or ansible — grouped into OS Setup, Install, Configure, Scripts and Cleanup.

  • Built-in palette plus My blocks — search, drag, drop.
  • Per-block inputs in the inspector; flag any as ask on deployment.
  • View YAML to see exactly what gets compiled.
localhost:8080/build/templates
GoblinDock block builder
One-click deploy

Pick a template. Name it. Deploy.

The deploy modal asks only what the template flagged — a fresh hostname, a password — shows live capacity on the target node, and builds the VM fresh from the base image.

  • Live RAM / cores / disk free on the target node.
  • Only the inputs marked ask on deployment.
  • Tweak the size, or take the template's defaults.
localhost:8080/operate/vms
GoblinDock deploy modal
Live jobs

Watch every deploy happen, live.

Each long action is a job: a phase bar, a step checklist, and the cloud-init + Ansible log streaming over SSE — download progress included while a node pulls a cloud image.

  • Phase bar and per-step checklist.
  • The exact Ansible log, line by line.
  • Queued jobs say which job they're waiting on.
localhost:8080/operate/jobs
GoblinDock live deploy job
The dashboard

Every VM you're allowed to see, one screen.

Cards or table, filter by status, search by name — auto-refreshing and role-filtered, with live CPU / RAM / disk sparklines on every card.

  • Running / working / error counts at a glance.
  • Deploy VM from anywhere — it's always one click away.
  • Read-only state endpoint — no live-probing other users' VMs.
localhost:8080/operate/vms
GoblinDock dashboard
The VM detail page

Live metrics, snapshots, and a real console.

Open any VM for live CPU / RAM / disk, full config, guest-agent OS & network info, and the deployment log. Snapshot before a risky change and roll back in seconds.

  • noVNC graphical + xterm.js serial console, proxied.
  • Snapshot / roll back / delete — optionally with RAM.
  • Start · stop · restart · rebuild · destroy.
localhost:8080/operate/vms/ai-dev-01
GoblinDock VM detail page
Goblins guard their hoard

Self-hosted, and hardened end to end.

GoblinDock went through a security & correctness review, independently verified. Your tokens never leave the box.

Encryption at rest

Secrets and Proxmox tokens are Fernet-encrypted (HKDF-derived from your secret key) and masked in logs and previews.

Tenant isolation

/api/state is role-filtered. A non-admin only ever receives their own VMs, a redacted connection view (no host, token id or SSH paths) and a redacted network view.

Guard rails

A hard VMID-window check (default 8000–8099) keeps GoblinDock off any VM outside its range, plus an SSRF guard on image URLs. Inputs are data, not code — every value is shell- and YAML-quoted.

Jobs fail loudly

A failed Proxmox task fails the job instead of logging success — so the database never advances past a VM that wasn't actually created.

Hardened container

Non-root, digest-pinned base, version-pinned deps and pinned Ansible collections. CSRF tokens, security headers, CSP, self-hosted fonts.

Rate-limited auth

Signed, httpOnly session cookies with session versioning — a password change revokes every other session. Password policy enforced; disabled accounts rejected on every path, including the console WebSockets.

Quickstart

Up and running in three commands.

GoblinDock ships as one multi-arch image on GHCR — no build step, no database server. All you bring is Docker, a secret key, and a folder to keep your data in.

Image ghcr.io/vladoportos/goblindock:latest multi-arch · tags latest · main · 2.2.0
  1. 1

    Generate a secret key

    It signs your sessions and derives the at-rest encryption key for your Proxmox tokens — so it must be strong and stay stable (rotate it and the encrypted secrets become unreadable). Drop it into a .env file beside your compose file.

    $ echo "GOBLINDOCK_SECRET_KEY=$(openssl rand -hex 32)" > .env
  2. 2

    Save a docker-compose.yml

    A minimal, production-leaning file: the GHCR image, your secret, guard-rail VMID + sizing caps, and a ./data bind mount that holds the SQLite database and cloud-image cache. Back up that one folder and you've backed up everything.

    docker-compose.yml
    services:
      goblindock:
        image: ghcr.io/vladoportos/goblindock:latest
        container_name: goblindock
        # run non-root as your host user so ./data stays yours (id -u / id -g)
        user: "${GD_UID:-1000}:${GD_GID:-1000}"
        ports:
          # loopback only — front it with a TLS reverse proxy before exposing it
          - "127.0.0.1:8080:8080"
        environment:
          # REQUIRED. Strong & STABLE — signs sessions + encrypts secrets at rest.
          GOBLINDOCK_SECRET_KEY: "${GOBLINDOCK_SECRET_KEY:?set a strong key in .env}"
          # guard rails — GoblinDock only ever touches VMs inside this window
          GOBLINDOCK_VMID_MIN: "8000"
          GOBLINDOCK_VMID_MAX: "8099"
          GOBLINDOCK_MAX_CORES: "1"
          GOBLINDOCK_MAX_RAM_MB: "2048"
        volumes:
          # persistent storage: SQLite DB + downloaded cloud-image cache
          - ./data:/data
        # container hardening — no caps, no privilege escalation
        cap_drop: [ALL]
        security_opt: ["no-new-privileges:true"]
        restart: unless-stopped
  3. 3

    Bring up the stack

    Compose pulls the image and starts the container on 127.0.0.1:8080. The first load asks you to create the admin account — then you're building templates.

    $ docker compose up -d

    Browse to http://127.0.0.1:8080 · building from source instead of pulling? add --build.

Knobs that matter

Everything else has a sane default. These are the ones worth knowing on day one.

GOBLINDOCK_SECRET_KEYrequired

Signs sessions and derives the at-rest encryption key. No default — a weak or empty value fails fast on boot. Keep it stable for the life of the install.

GOBLINDOCK_VMID_MIN / _MAX

The only VMID window GoblinDock is ever allowed to touch. Defaults to 80008099.

GOBLINDOCK_MAX_CORES / _RAM_MB

Per-VM sizing ceilings enforced on every deploy. Defaults to 1 core / 2048 MB.

GD_UID / GD_GID

Host uid/gid the container runs as, so the bind-mounted ./data (and SSH key) stay readable. Set to your id -u / id -g.

GOBLINDOCK_FORWARDED_ALLOW_IPS

Behind a reverse proxy? Set this to the proxy's IP/CIDR so X-Forwarded-For is trusted for throttling and the audit log.

./data:/datavolume

Persistent storage — the SQLite DB plus the cached cloud images. Host-visible and the single path you need to back up.

Want the Proxmox API token mounted as a file secret (invisible to docker inspect), or a node SSH key for cloud-init snippet baking and IP detection? The full compose file on GitHub ships both, commented and ready.

Put the goblin to work.

One container, one SQLite file, one folder to back up. Star the repo, then go stack some blocks.