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.
One container. Everything inside.
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.
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 imageStack 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 + sizePick 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 inputsProvisioning, configuration, lifecycle and a real console — bundled into one panel your whole household (or team) can share.
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.
The same noVNC console Proxmox uses, plus an xterm.js serial tab — proxied so the browser only ever talks to GoblinDock.
Every long action is a job with a step checklist, progress bar and a streaming log. Watch the goblin work in real time.
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.
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.
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.
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.
# one script, runs as root set -e echo '>>> GoblinDock: Base OS Setup' timedatectl set-timezone UTC localectl set-locale LANG=en_US.UTF-8
- 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.
A vendored React SPA — no build step — served straight from the container. Mono-forward, goblin-gold, dark by default.
Compose a template from typed blocks — each tagged cloud-init or ansible — grouped into OS Setup, Install, Configure, Scripts and Cleanup.
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.
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.
Cards or table, filter by status, search by name — auto-refreshing and role-filtered, with live CPU / RAM / disk sparklines on every card.
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.
GoblinDock went through a security & correctness review, independently verified. Your tokens never leave the box.
Secrets and Proxmox tokens are Fernet-encrypted (HKDF-derived from your secret key) and masked in logs and previews.
/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.
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.
A failed Proxmox task fails the job instead of logging success — so the database never advances past a VM that wasn't actually created.
Non-root, digest-pinned base, version-pinned deps and pinned Ansible collections. CSRF tokens, security headers, CSP, self-hosted fonts.
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.
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.
ghcr.io/vladoportos/goblindock:latest
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
docker-compose.ymlA 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.
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
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.
Everything else has a sane default. These are the ones worth knowing on day one.
GOBLINDOCK_SECRET_KEYrequiredSigns 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 / _MAXThe only VMID window GoblinDock is ever allowed to touch. Defaults to 8000–8099.
GOBLINDOCK_MAX_CORES / _RAM_MBPer-VM sizing ceilings enforced on every deploy. Defaults to 1 core / 2048 MB.
GD_UID / GD_GIDHost 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_IPSBehind a reverse proxy? Set this to the proxy's IP/CIDR so X-Forwarded-For is trusted for throttling and the audit log.
./data:/datavolumePersistent 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.
One container, one SQLite file, one folder to back up. Star the repo, then go stack some blocks.