No description
  • Go 98.6%
  • Shell 1.1%
  • Makefile 0.3%
Find a file
2026-07-17 21:30:34 +02:00
.vscode fix: update fileNesting config 2026-07-16 12:21:06 +02:00
assets add logo 2026-03-13 14:36:53 +01:00
cli chore(templates): refresh SOPS onboarding docs 2026-07-17 21:30:34 +02:00
dozzle_data doc: better users.example.yml for dozzle 2026-07-16 12:21:31 +02:00
scripts fix(server): preserve manual Beszel agent deployment 2026-07-17 20:31:20 +02:00
.dockiy.env.example refactor: rename DOCKIY_REMOTE_ROOT to DOCKIY_REMOTE_PATH 2026-07-16 12:35:08 +02:00
.enc.env ops: add beszel env keys 2026-07-16 12:54:33 +02:00
.env.example docs(secrets): reserve DockIY key namespace 2026-07-17 21:14:52 +02:00
.gitignore chore(config): ignore local server settings 2026-07-17 20:58:26 +02:00
.sops.yaml feat: update .env with sops 2026-07-16 12:01:09 +02:00
docker-compose.yml fix(serverinit): pin base stack image versions 2026-07-17 20:55:37 +02:00
LICENSE Add LICENSE 2025-11-16 18:16:49 +01:00
README.md docs(secrets): reserve DockIY key namespace 2026-07-17 21:14:52 +02:00

DockIY logo

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.