- Go 98.6%
- Shell 1.1%
- Makefile 0.3%
| .vscode | ||
| assets | ||
| cli | ||
| dozzle_data | ||
| scripts | ||
| .dockiy.env.example | ||
| .enc.env | ||
| .env.example | ||
| .gitignore | ||
| .sops.yaml | ||
| docker-compose.yml | ||
| LICENSE | ||
| README.md | ||
DockIY
DockIY is a minimal, open-source toolkit for running several web apps on one self-owned VPS. It combines a transparent Docker Compose base stack with a Go CLI for server setup, app scaffolding, immutable releases, encrypted secrets, deployment history, status, and rollback.
The base stack includes:
- Traefik for reverse proxying and TLS;
- a VPS-local Docker registry for application images;
- Umami for analytics;
- Dozzle for logs;
- Beszel for monitoring and notifications.
You own the server, configuration, application code, images, Compose files, lifecycle scripts, and operational history. DockIY is a safety and convenience layer over familiar tools, not a hosted platform or hidden control plane.
Requirements
The VPS needs Docker with the Compose plugin and rsync. Create a dedicated SSH user, add it to the Docker group, and make sure it can run Docker without sudo. DNS must point the base-stack and application hosts at the VPS.
The deployment machine needs Go 1.23 or newer when installing from source, plus Bash, Docker with the Compose plugin, Git, SSH, and rsync. Install SOPS and age when an app or the manual base-stack workflow uses encrypted environment files.
Install the CLI
cd cli
make install VERSION=v1.0.0
dockiy version
The install target writes to $GOBIN, or $GOPATH/bin when $GOBIN is unset.
Add that directory to PATH. make build creates one host binary and
make release VERSION=v1.0.0 creates Linux/macOS amd64/arm64 binaries under
cli/dist. These targets embed the release version, source commit, and UTC build
date displayed by dockiy version; a plain go install ./cmd/dockiy reports
development metadata.
Configure DockIY
Create a global server profile interactively:
dockiy setup
For unattended setup, provide the profile and SSH destination explicitly:
dockiy setup --non-interactive --server personal --ssh dockiy-vps
dockiy config validate
The platform config directory holds named VPS profiles. On Linux the default is
~/.config/dockiy/config.yml. The built-in remote path, target platform, local
registry tunnel port, and VPS registry port are dockiy, linux/amd64, 5500,
and 5000. See cli/config.example.yml for global
overrides and cli/dockiy.local.example.yml for
uncommitted checkout-specific overrides.
An SSH alias is recommended because ~/.ssh/config can own the address, port,
identity, proxy, and host-key behavior. Every environment in one app resolves to
the same declaratively selected server. There is no deployment --server flag.
Global machine/VPS configuration does not belong in an app-local
.dockiy.env. Older application templates using that pattern should migrate to
the global config plus optional uncommitted dockiy.local.yml.
Initialize the base stack
Run server initialization after the selected SSH user, Docker, rsync, and DNS
are ready. The CLI deploys its pinned embedded base stack to the profile's
remote_path, creates the shared reverse_proxy network and directories
idempotently, configures Dozzle authentication, secures Umami's default
administrator, and registers the local Beszel agent.
The uploaded docker-compose.yml, .env, Dozzle users file, protected Beszel
agent environment, volumes, and data directories remain visible on the VPS.
Re-running initialization preserves initialized service identities and refuses
unknown credentials instead of resetting data.
Create a mode-0600 server-init.yml containing base-stack hosts, database
credentials, desired Umami administrator, Beszel user, and the path to a
mode-0600 Dozzle users file, then run:
dockiy server init --settings server-init.yml
dockiy server init --status
The mutation command reads secrets only from the strict YAML file and reports
retryable phases. Status needs no settings and reports complete only when all
seven base services are running. Server initialization targets default_server
from the selected global config; there is no --server option.
The registry listens only on VPS loopback, port 5000 by default. It is not
exposed through Traefik and has no separate registry authentication; SSH is the
authentication boundary. Application deployment opens a local SSH forward for
pushes, while the VPS Docker daemon pulls from its own loopback registry.
See the CLI guide for the exact settings schema, file permission requirements, registration policy, and idempotency behavior.
Scaffold an app
dockiy app init react my-app
dockiy app init vitepress docs-site
dockiy app init nuxt full-stack-app
Verified React, VitePress, and Nuxt templates are embedded in the CLI release, so scaffolding is offline and version-matched. DockIY writes a normal directory without template Git history and never initializes or commits a repository.
Application repositories own their Dockerfiles, Compose files, Traefik labels,
hosts, optional encrypted secrets, and lifecycle hooks. A VitePress-style app
can have one production-like default environment. React and Nuxt-style apps
can have an isolated staging and production pair.
Deploy releases
Run app commands from the application repository. Default and production use an explicit SemVer and require a clean Git worktree:
dockiy deploy --version v1.2.5
dockiy deploy production --version v1.2.5
Staging derives its base from the latest production tag, or accepts an explicit base before the first production release:
dockiy deploy staging
dockiy deploy staging --version v1.2.5
DockIY creates immutable Git tags (default/v1.2.5, prod/v1.2.5, or a dated
staging/v1.2.5-YYYYMMDD-NN), builds app images locally, pushes through the SSH
tunnel, uploads an immutable deployment bundle, runs app-owned hooks, verifies
health, and records the result on the VPS. Existing Git and registry tags are
never silently overwritten.
After all local hooks finish, DockIY reinspects Git before the first push. Default/production stop if HEAD or worktree cleanliness changed. Staging also stops if HEAD changed; cleanliness drift is allowed only with a warning and a conservative dirty ledger record. Every declared artifact's image digest is captured in the ledger and immutable bundle.
The legacy dockiy deploy ... --tag <tag> path remains temporarily available
with a prominent warning. It bypasses release metadata, SOPS, the full hook
lifecycle, ledger, and rollback guarantees and should not be used for new
deployments.
SOPS app secrets
An environment may declare an encrypted dotenv file in dockiy.yml. DockIY
templates retain these names so SOPS recognizes dotenv format from .env:
.enc.default.env
.enc.staging.env
.enc.production.env
The CLI decrypts only the selected environment into a permission-restricted
local temporary directory, rejects every app key in the CLI-owned DOCKIY_*
namespace, merges deployment metadata into deploy.env, uploads it with
restrictive permissions, and removes the local plaintext on every exit path.
Normal application keys remain allowed. The age private identity stays local.
Status and rollback
dockiy status
dockiy status production --history 10 --live
dockiy rollback production --to prod/v1.2.4
The remote ledger records successful and failed deploy/rollback attempts and
updates current state atomically only after start and health verification.
--live adds a clearly labeled docker compose ps observation over SSH.
Rollback verifies every artifact digest and restores the preserved bundle
without rebuilding. The bundle's immutable .dockiy-bundle.json supplies its
release identity, hook environment, artifact identities, migration flag, and
the rollback-safe hooks to run; the current checkout cannot redefine them.
Bundles created by older DockIY versions without .dockiy-bundle.json are
intentionally refused. After upgrading, complete one fresh deployment in each
environment to establish its first compatible rollback target.
DockIY does not reverse database migrations. Apps that declare migrations emit a schema-compatibility warning before rollback.
Manual base-stack deployment
The CLI is the supported orchestration path, but the repository keeps the
base-stack files and phase-level script visible for inspection and recovery.
This root-level .dockiy.env is only input to the legacy/manual base-stack
script; it is not the removed app-local server configuration pattern.
cp .dockiy.env.example .dockiy.env
cp .env.example .env
Set the SSH destination in .dockiy.env, fill the base-stack hosts and secrets
in .env, generate dozzle_data/users.yml, then run:
scripts/deploy.sh
The manual script derives a permission-restricted beszel-agent.env from
BESZEL_KEY and BESZEL_TOKEN, uploads it separately, and removes the local
temporary plaintext on every exit. The CLI bootstrap writes the same remote
file directly and does not retain those values in the base .env.
To avoid retaining the base-stack environment as plaintext, configure
.sops.yaml with one or more age recipients and keep the established .enc.env
name:
sops encrypt --output .enc.env .env
sops edit .enc.env
sops decrypt .enc.env
The manual script prefers .enc.env when present and decrypts it into a
permission-restricted temporary file. .sops.yaml, recipient public keys, and
.enc.env may be committed; age private identities and plaintext .env must
remain private.
Generate Dozzle file authentication with its containerized helper:
docker run -it --rm amir20/dozzle generate dockiy \
--password 'replace-with-a-strong-password' > dozzle_data/users.yml
Without CLI bootstrap, immediately replace Umami's public admin / umami
login through the Umami UI. For Beszel, create the Hub user, add a local system
using /beszel_socket/beszel.sock, store its generated key/token in the manual
base-stack .env, and redeploy. These UI steps are recovery mechanisms, not
required by successful CLI initialization.
Disaster recovery
Back up:
- application repositories and their Git tags;
- global and local DockIY configuration;
- SOPS configuration and age private identities in a separate secure location;
- the VPS DockIY
remote_path, including deployment ledger and bundles; - Docker volumes and databases;
- deployed environment files and ACME data.
The system remains manually operable with Git, SOPS, Docker, SSH, rsync, and Compose. You can inspect app manifests and hooks, tunnel to the loopback registry, sync a preserved bundle, run its scripts, and inspect the JSON/NDJSON ledger. DockIY intentionally does not automate VPS backups, database backups, DNS, schema rollback, zero-downtime releases, or multi-server orchestration.
See cli/README.md for the complete manifest, release, hook,
ledger, rollback, and manual-operation contracts.