๐Ÿพ freeholdy user guide

What is freeholdy

freeholdy is a single-VPS orchestrator for your Docker apps. It puts every project behind nginx + Let's Encrypt and serves each one at its own {name}.your_domain.com subdomain โ€” with HTTPS wired up automatically.

You push code (a folder, a git URL, or a pre-packaged plugin); freeholdy auto-detects whether it's a single-container Dockerfile project or a multi-container docker-compose.yml stack (compose wins when both are present), builds it, runs it, and wires up the reverse proxy and SSL certificate. No per-project nginx or certbot configuration to write.

There are three ways to drive it:

  • CLI โ€” fhcli, a small command-line client (used in the examples below).
  • Web UI โ€” a control panel at ui.<your-domain> covering the same operations in the browser.
  • REST API โ€” everything above is a thin client over the API at api.<your-domain>, authenticated with a bearer token.

Installation

Prerequisites

  • A fresh Ubuntu VPS with root access. The installer handles all packages itself.
  • A base domain with a wildcard DNS record (*.your_domain.com) pointing at the server โ€” projects are served at {name}.your_domain.com and the API at api.your_domain.com.

One-command bootstrap

bash <(curl -fsSL https://raw.githubusercontent.com/aafanasev-dev/freeholdy/main/install.sh)

On a server that already runs other apps, clone the repo and run sudo bash install.sh instead. The installer auto-detects one of two modes and asks you to confirm before touching anything:

  • FRESH โ€” docker and/or nginx are missing โ†’ it installs what's missing. For a dedicated or empty VPS.
  • COEXIST โ€” both are already present โ†’ it never installs, restarts, or upgrades docker or nginx, and aborts early if your existing nginx config is broken. Safe next to other apps.

The installer then:

  1. Prompts for a service user (default freeholdy), your base domain, and a Let's Encrypt email.
  2. Sets up the service user with docker access and passwordless nginx/certbot sudo.
  3. Creates the Python venv, writes .env, and picks a free local API port.
  4. Adds the api.<domain> nginx vhost, obtains its SSL certificate, and installs a nightly renewal cron.
  5. Installs and starts the freeholdy systemd service.
  6. Prints your first API token โ€” shown once; save it.

Flags: -u USER (service user), -y (assume yes), -r (redo every step). Progress is tracked in install.log, so re-running is idempotent โ€” it skips finished steps and can, for example, re-enable SSL after DNS propagates.

Set up the CLI

On your workstation (or the server), from a clone of the repo:

cd cli/
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env   # set TOKEN and BASE_DOMAIN

Check it works with ./fhcli.py health.

Deploying apps

There is no separate "create project" step โ€” the first deploy creates the project, and re-running the same deploy redeploys it. Every deploy streams its build log live to your terminal.

From a folder

fhcli deploy myapp ./myapp

Uploads the folder, auto-detects the manifest โ€” a Dockerfile (it must EXPOSE a port) or a docker-compose.yml โ€” builds it, runs it, and wires up nginx + SSL at myapp.your_domain.com. Compose stacks get one subdomain per exposed service ({service}.myapp.your_domain.com). Re-run the command to redeploy.

From git

fhcli deploy mysite https://github.com/owner/repo.git
fhcli deploy mysite git@github.com:owner/repo.git --branch dev

Clones the repo on the server and runs the exact same detect โ†’ build โ†’ run โ†’ nginx pipeline. Re-run to redeploy the latest commit.

For private repos, run fhcli get-git-key โ€” it prints the server's SSH public key (generated on first use); add it as a deploy key on the repo, then deploy over the git@โ€ฆ URL.

From plugins

fhcli plugins                       # list available plugins
fhcli plugin-add nextcloud mycloud  # install one as project "mycloud"

Plugins are pre-packaged apps that deploy through the same pipeline. Some are interactive: the install prompts you right in the terminal (e.g. to choose an admin account) before the build starts. If your terminal disconnects mid-install, re-running plugin-add resumes it.

Plugin catalog

โ˜๏ธ nextcloud interactive

Nextcloud file sync & share at nextcloud.<domain> (app, postgres, redis, cron). The install prompts for the admin account.

๐Ÿ“ฎ mailserver interactive

Full e-mail server (docker-mailserver) โ€” SMTP, IMAP, and DKIM at mail.<domain>. Choose addons and the first mailbox during install.

โœ‰๏ธ snappymail interactive

SnappyMail webmail โ€” a web UI for your mail server at mailui.<domain>. Pairs with the mailserver plugin.

๐ŸŽฅ jitsi-meet interactive ยท UDP 10000

Jitsi Meet video conferencing at meet.<domain>. Choose the moderator account during install; UDP 10000 must be reachable from the internet.

๐Ÿ–ผ๏ธ imgstore

Token-gated image storage โ€” upload via a web UI, share via public links with custom names or auto sha256 URLs.

๐Ÿ“‚ sftp-go interactive

Personal SFTPGo file server โ€” SFTP, WebDAV & WebClient over your own folder.

๐Ÿ›ก๏ธ amneziavpn interactive ยท UDP port

AmneziaWG (DPI-resistant WireGuard) VPN server on a raw UDP port โ€” no subdomain, no SSL. Pick the port, client DNS, and first client during install; your firewall must allow the UDP port in.

๐Ÿ”‘ outline interactive ยท 2 ports

Outline Server (Shadowbox) โ€” self-hosted Shadowsocks VPN managed from the Outline Manager desktop app. Pick the API and access-key ports during install; your firewall must allow both in.

๐Ÿ“– freeholdy-help

This guide โ€” a static page that also doubles as a smoke test for a fresh install.

Versions & rollback

Every deploy is blue/green versioned: the new version is built and verified running before traffic switches to it, so a failed build never takes the live site down. Previous versions are kept for rollback.

fhcli versions myapp            # list versions: active / inactive / archived
fhcli rollback myapp 2          # make version 2 live again
fhcli set-backup-limit myapp 3  # keep at most 3 archived versions
  • active โ€” the running version nginx points at.
  • inactive โ€” the previous version, kept as a stopped container for instant rollback (Dockerfile projects only).
  • archived โ€” older versions with their image retained, capped by the backup limit (default 5, oldest pruned first).
Compose rollback restores the project's code snapshot and images, then brings the stack back up โ€” but named-volume data is not rolled back. Databases keep their current contents.

AI integration

freeholdy ships a Claude Code skill, fhdeploy, that lets Claude deploy to your server for you โ€” no fhcli required. It talks to the REST API directly: uploads a folder (or points the server at a git URL), follows the build, and reports the live endpoints.

Setup

  1. Copy skills/fhdeploy/ from the freeholdy repo into your Claude Code skills directory (e.g. ~/.claude/skills/).
  2. Run it from any Claude Code session:
    /fhdeploy myapp ./myapp
    /fhdeploy mysite https://github.com/owner/repo.git
  3. On first use it asks for your API token (printed by the installer) and base domain, and remembers them in Claude Code memory for next time.

Beyond deploys, the skill knows the versioning API โ€” it can list a project's versions and roll back โ€” and the git deploy-key flow for private repos. In practice: tell Claude to build something, then tell it to ship it.