{"id":51051631,"url":"https://github.com/felipefontoura/bento","last_synced_at":"2026-06-22T17:32:35.170Z","repository":{"id":268218789,"uuid":"896249136","full_name":"felipefontoura/bento","owner":"felipefontoura","description":"Bento turns a fresh Ubuntu/Debian VPS into a hardened Docker Swarm server with Traefik, Portainer, TLS, and deployable apps through a guided terminal menu.","archived":false,"fork":false,"pushed_at":"2026-06-19T04:34:29.000Z","size":723,"stargazers_count":15,"open_issues_count":8,"forks_count":20,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-19T06:05:07.468Z","etag":null,"topics":["automation","devops","docker-swarm","portainer","self-hosted","server-hardening","traefik","vps-setup"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/felipefontoura.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-11-29T21:52:34.000Z","updated_at":"2026-06-19T04:34:27.000Z","dependencies_parsed_at":null,"dependency_job_id":"8f654337-1982-40e1-a970-a0110d9eb0af","html_url":"https://github.com/felipefontoura/bento","commit_stats":null,"previous_names":["felipefontoura/docker-swarm-stacks","felipefontoura/quickstack"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/felipefontoura/bento","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipefontoura%2Fbento","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipefontoura%2Fbento/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipefontoura%2Fbento/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipefontoura%2Fbento/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/felipefontoura","download_url":"https://codeload.github.com/felipefontoura/bento/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/felipefontoura%2Fbento/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34659896,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-22T02:00:06.391Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automation","devops","docker-swarm","portainer","self-hosted","server-hardening","traefik","vps-setup"],"created_at":"2026-06-22T17:32:34.313Z","updated_at":"2026-06-22T17:32:35.163Z","avatar_url":"https://github.com/felipefontoura.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"![bento](.assets/bento-banner.png)\n\n_Your VPS, served on a tray._\n\n![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)\n![Made with Bash](https://img.shields.io/badge/made%20with-bash-1f425f.svg)\n![Docker Swarm](https://img.shields.io/badge/docker-swarm-2496ED.svg?logo=docker\u0026logoColor=white)\n[![Stars](https://img.shields.io/github/stars/felipefontoura/bento?style=flat)](https://github.com/felipefontoura/bento/stargazers)\n[![Last commit](https://img.shields.io/github/last-commit/felipefontoura/bento)](https://github.com/felipefontoura/bento/commits/main)\n\n[Quickstart](#quickstart) · [Maintainers](CLAUDE.md) · [Issues](https://github.com/felipefontoura/bento/issues)\n\n---\n\n## Quickstart\n\n```bash\nbash \u003c(curl -sSL https://raw.githubusercontent.com/felipefontoura/bento/stable/boot.sh)\n```\n\n## TL;DR\n\nPaste that on a fresh Ubuntu/Debian VPS, answer three questions (domain, admin email, public IP), and ~15 minutes later you have a hardened host, Traefik + Portainer with TLS, the apps you picked, and an HTML handoff report. Already know bento? Copy and go. Want context? Keep reading.\n\n\u003e **Prefer to drive it from Claude Code instead of the server console?** Install the `bento` plugin and Claude does the whole flow over SSH — you never SSH in by hand or touch the bento menu. See [Install with Claude Code](#install-with-claude-code-no-server-console). Ideal for beginners (works on Windows) and for consultants setting up a client's VPS: hand Claude an SSH-reachable host + a domain + an app list and pick up at the end with credentials + URLs.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eTable of contents\u003c/strong\u003e\u003c/summary\u003e\n\n- [Quickstart](#quickstart)\n- [Install with Claude Code](#install-with-claude-code-no-server-console)\n- [What is bento](#what-is-bento)\n- [How it works](#how-it-works)\n- [Stacks](#stacks)\n- [Prerequisites](#prerequisites)\n- [After install](#after-install)\n- [Operations](#operations)\n- [For maintainers and contributors](#for-maintainers-and-contributors)\n\n\u003c/details\u003e\n\n---\n\n## What is bento\n\n**A guided installer that turns a fresh VPS into a hardened, TLS-fronted Docker Swarm with the apps you want — in under 15 minutes, with one terminal command.**\n\nYou answer three questions (domain, admin email, public IP), walk through three guided steps (harden, infra, apps), and finish with a single HTML report you can hand off to a client. Every secret is generated for you; nothing is hardcoded.\n\n---\n\n## How it works\n\nA one-time bootstrap captures **`BASE_DOMAIN`**, **`ADMIN_EMAIL`**, and your **VPS public IP** (auto-detected). Then three idempotent, re-runnable steps:\n\n### Step 1 — Harden the system\n\nAn embedded copy of the [ubinkaze](https://github.com/felipefontoura/ubinkaze) hardening script:\ninstalls Docker, applies kernel sysctl + UFW (with `limit ssh`) + fail2ban + AppArmor + AIDE + auditd + chrony,\ncreates a `docker` user with your SSH keys, then initializes Docker Swarm and the `network_public` overlay. Requires one reboot.\n\n### Step 2 — Install infrastructure\n\nNo prompts. Deploys Traefik (Let's Encrypt + HTTPS redirect) and Portainer at `portainer.\u003cyour-domain\u003e`, generates a strong Portainer admin password, displays it once.\n\n### Step 3 — Install applications\n\nPick from a checklist. For each stack, bento:\n\n1. **Prompts only for env vars without sensible defaults.** Hostnames default to `\u003ckey\u003e.\u003cyour-domain\u003e`; secrets are auto-generated; DB passwords are reused from the postgres stack.\n2. **Deploys via Portainer's API as a Git-backed stack** so Portainer becomes the canonical source of truth for the running spec.\n3. **Runs an optional `install.sh`** for post-deploy bootstrap (DB creation, migrations).\n4. **Prints the URL** — you open and log in.\n\nWhen Paperclip is in the deploy set, Step 3 offers an optional **Authenticate AI providers** step right after a successful deploy so the agent runtime can immediately call out to Claude / OpenAI Codex. The same menu is available any time from the main menu, or directly as `bento-auth` on the host. Full details in [docs/reference/bento-auth.md](docs/reference/bento-auth.md).\n\n---\n\n## Install with Claude Code (no server console)\n\nEverything above — Bootstrap, Step 1, Step 2, Step 3, the post-hardening reboot,\nthe unattended env vars, the recovery when something stalls — is **drudgery you\nshouldn't have to do by hand** if you \"just want to use it.\" The `bento` plugin\nfor Claude Code drives all of it over SSH. You never log into the server console\nor touch bento's menu: you talk to Claude, it does the rest and hands you back\nURLs + credentials.\n\n**You need:** Claude Code, an SSH key on the VPS, and wildcard DNS (see\n[Prerequisites](#prerequisites)). Nothing is installed locally except Claude\nCode — Docker/Swarm/apt all run on the VPS.\n\n**Install the plugin once** — type these **inside Claude Code** (not your shell),\n**one at a time**. They are two separate slash commands; run the first, wait for\nit to confirm, then run the second. Don't paste both lines together — that\nconcatenates them into one broken command.\n\n1. Add the marketplace:\n\n   ```\n   /plugin marketplace add felipefontoura/bento\n   ```\n\n2. Then install the plugin:\n\n   ```\n   /plugin install bento@felipefontoura\n   ```\n\n**Then just ask.** The plugin adds these `/bento:*` skills.\n\n**Lifecycle** — get the box and its apps running:\n\n| Command | What Claude does over SSH |\n|---|---|\n| `/bento:install` | Fresh VPS → hardened host + Traefik/Portainer + your apps. Runs Step 1/2/3 unattended, rides through the reboot, recovers from known failures, reports URLs + Portainer login. |\n| `/bento:deploy` | Add or redeploy apps on a server that already runs bento. |\n| `/bento:update` | Pull the latest bento and redeploy your stacks. |\n| `/bento:status` | Read-only health check (services, HTTPS, disk/memory). |\n| `/bento:auth` | Register an AI-provider API key and propagate it to your stacks. |\n\n**Operate** — day-2 work *inside* the deployed apps, through each one's API.\nYou don't invoke these by name: Claude auto-loads the right one when you describe\nthe task in plain language (*\"send a WhatsApp\"*, *\"reply to a customer\"*,\n*\"build a workflow\"*). Each discovers the host/credentials from bento state — it\nnever hardcodes your instance.\n\n| Command | Operate… | Talk to it like |\n|---|---|---|\n| `/bento:paperclip` | Paperclip agent orchestration — agents, instruction bundles, skills, board issues. | *\"create an agent / import skills / clean up the board\"* |\n| `/bento:hermes` | Hermes agent gateway — chat over the OpenAI-compatible API, run the CLI, wire MCP servers. | *\"talk to my agent / give it the youtube tools\"* |\n| `/bento:n8n` | n8n workflows, driven through the n8n-mcp tools (schemas + validation baked in). | *\"build / fix / run a workflow\"* |\n| `/bento:evolution-api` | Evolution API — WhatsApp instances, QR pairing, send messages, webhooks. | *\"connect my WhatsApp / send a message\"* |\n| `/bento:chatwoot` | Chatwoot support desk — conversations, replies, contacts, inboxes. | *\"reply to a customer / list open chats\"* |\n| `/bento:typebot` | Typebot chatbots — start/continue chats, publish, read results (builder vs viewer). | *\"start a bot chat / get results\"* |\n| `/bento:plunk` | Plunk transactional email — send, track events, manage contacts (AWS SES behind it). | *\"send an email / track an event\"* |\n| `/bento:metamcp` | MetaMCP gateway — group MCP servers into namespaces + endpoints, mint keys. | *\"add an MCP server / get my tools endpoint\"* |\n\nExample: *\"`/bento:install` on root@198.51.100.42, domain example.com, apps n8n\nand chatwoot\"* → Claude takes it from there. Later: *\"send a WhatsApp to +55…\nsaying the order shipped\"* → Claude loads `/bento:evolution-api` on its own.\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eWindows\u003c/strong\u003e — the only local requirement is a working \u003ccode\u003essh\u003c/code\u003e\u003c/summary\u003e\n\nDocker and everything else run on the VPS, so locally you just need Claude Code\n+ `ssh`. The lowest-friction path is **WSL2**, where the commands are identical\nto macOS/Linux:\n\n```powershell\nwsl --install        # PowerShell as admin, then reboot\n```\n```bash\n# inside the Ubuntu/WSL terminal:\nssh-keygen -t ed25519                       # paste the .pub into Hetzner\ncurl -fsSL https://claude.ai/install.sh | bash\nclaude                                       # then: /plugin marketplace add … / /plugin install …\n```\n\nNative Windows works too (`irm https://claude.ai/install.ps1 | iex`, plus Git\nfor Windows for the Bash tool); if `ssh` complains about key permissions, run\n`icacls \"$env:USERPROFILE\\.ssh\\id_ed25519\" /inheritance:r /grant:r \"$env:USERNAME`:F\"`.\n\u003c/details\u003e\n\n---\n\n## Stacks\n\nEach stack is a directory at `stacks/\u003ccategory\u003e/\u003ckey\u003e/` with `compose.yml`, `manifest.json`, and optionally `install.sh`. Adding a new stack is documented in [CLAUDE.md](CLAUDE.md).\n\n| Category | Stack | What it is |\n|---|---|---|\n| infra | [Traefik](stacks/infra/traefik) | Reverse proxy + Let's Encrypt |\n| infra | [Portainer](stacks/infra/portainer) | Stack manager UI |\n| db | [PostgreSQL](stacks/db/postgres) | Each app creates its own database in `install.sh` |\n| db | [Redis](stacks/db/redis) | In-memory cache |\n| app | [Chatwoot](stacks/app/chatwoot) | Customer support platform |\n| app | [CLI Proxy API](stacks/app/cli-proxy-api) | OpenAI-compatible proxy in front of CLI providers |\n| app | [Crawl4AI](stacks/app/crawl4ai) | Headless web crawler/extractor (internal-only). Optional outbound proxy to dodge datacenter-IP anti-bot blocks — see [docs/reference/crawl4ai-proxy.md](docs/reference/crawl4ai-proxy.md) |\n| app | [Evolution API](stacks/app/evolution-api) | WhatsApp gateway |\n| app | [Hermes](stacks/app/hermes) | Seeds a shared volume with Hermes Agent so Paperclip's hermes_local adapter can exec the CLI locally (overlay-only, idle sleep, no gateway) |\n| app | [MetaMCP](stacks/app/metamcp) | MCP aggregator/gateway — unify multiple MCP servers (stdio + HTTP) behind one endpoint, with a web admin UI |\n| app | [n8n](stacks/app/n8n) | Workflow automation |\n| app | [n8n MCP](stacks/app/n8n-mcp) | MCP server for n8n |\n| app | [Openclaw](stacks/app/openclaw) | Personal AI assistant with a web Control UI — sign in with your ChatGPT/Claude subscription, chat, connect a Telegram bot, no terminal. OpenAI-compatible API kept for overlay consumers |\n| app | [Paperclip](stacks/app/paperclip) | AI agent orchestration (Claude Code, Codex, OpenCode, Hermes via hermes-bin volume) |\n| app | [Plunk](stacks/app/plunk) | Open-source email platform |\n| app | [RabbitMQ](stacks/app/rabbitmq) | Message broker |\n| app | [Typebot](stacks/app/typebot) | Chatbot builder |\n\n---\n\n## Prerequisites\n\n### VPS\n\n\u003c!-- TODO: replace YOUR_REF with the real Hetzner affiliate code before pushing. --\u003e\n\n| Partner | When | Plan | Link |\n|---|---|---|---|\n| **Hetzner** (primary) | EU/US users — bento is smoke-tested against it every release | CX22, latest Ubuntu LTS | [hetzner.cloud](https://hetzner.cloud/?ref=YOUR_REF) |\n| Hostinger (secondary) | Brazil-based users — BRL billing, low BR latency | KVM 2+, latest Ubuntu LTS | [hostinger.com/br/smartdev](https://hostinger.com/br/smartdev) |\n\n\u003cdetails\u003e\n\u003csummary\u003eAffiliate disclosure (read once, applies to both)\u003c/summary\u003e\n\nBoth links above are affiliate referrals. Signing up through them gives bento a small commission that funds new stacks and bug fixes — there is **no premium price** for you and **no functional difference** from a direct signup. If you'd rather not contribute, just visit [hetzner.com](https://hetzner.com) or [hostinger.com](https://hostinger.com) directly and the installer works identically. Same goes for any apt-based VPS (DigitalOcean, OVH, Vultr, your own metal).\n\n\u003c/details\u003e\n\n### DNS\n\nYou need a wildcard A record before Step 2, or Let's Encrypt fails on first boot:\n\n| Type | Name             | Value           |\n|------|------------------|-----------------|\n| A    | `*.mydomain.com` | `\u003cyour VPS IP\u003e` |\n\n(bento only uses subdomains. If you already have a website at the bare `mydomain.com`, leave its existing A/CNAME alone — the wildcard above won't touch it.)\n\n[**Open the DNS records page on Cloudflare →**](https://dash.cloudflare.com/?to=/:account/:zone/dns) — Cloudflare prompts you to pick the account + zone, then drops you straight onto the records page. Cloudflare is the recommended DNS host (free tier, fast); any provider works.\n\nVerify before Step 2:\n\n```bash\ndig +short A portainer.mydomain.com\n# should print your VPS IP\n```\n\n### Firewall\n\n| Layer | What it does | Setup |\n|---|---|---|\n| **Hetzner Cloud Firewall** | Edge filter; optional | Manual, in Hetzner panel |\n| **UFW + fail2ban** | Default-deny inbound, `limit ssh`, allow 80/443/ICMP | Automatic during Step 1 |\n\n\u003cdetails\u003e\n\u003csummary\u003eRecommended Hetzner Cloud Firewall ruleset\u003c/summary\u003e\n\nIn **Firewalls → Create Firewall → Apply to your server**:\n\n| Direction | Source | Port | Protocol | Why |\n|---|---|---|---|---|\n| Inbound | Your home IP | 22 | TCP | SSH — or leave open and let `ufw limit` + fail2ban handle brute-force |\n| Inbound | `0.0.0.0/0` | 80 | TCP | Let's Encrypt HTTP-01 + HTTPS redirect |\n| Inbound | `0.0.0.0/0` | 443 | TCP | HTTPS |\n| Inbound | `0.0.0.0/0` | any | ICMP | `ping` debugging |\n| Outbound | `0.0.0.0/0` | all | all | Default |\n\nIf you lock SSH to your home IP and your IP changes (mobile, ISP renewal), use Hetzner's web console to recover. For starter setups, leaving SSH open with `ufw limit` + fail2ban is a reasonable trade-off.\n\n\u003c/details\u003e\n\n---\n\n## After install\n\n### Handoff HTML\n\nWhen Step 3 finishes — or any time, from the **Report** menu — bento writes a self-contained HTML file with the VPS overview, Traefik + Portainer access, and every deployed stack's URL and resolved env vars. Secrets are masked by default with click-to-reveal; print to PDF auto-reveals everything for offline handoff.\n\n```\n~/.local/share/bento/reports/handoff-\u003ctimestamp\u003e.html       # chmod 600\n```\n\nMove it off the VPS:\n\n```bash\nscp user@vps:~/.local/share/bento/reports/handoff-*.html .\n```\n\n\u003e The report carries live credentials. Treat it like a password vault: deliver over an encrypted channel (1Password, Bitwarden Send, encrypted email), rotate if it ever leaks.\n\n### Ownership: bento vs Portainer\n\nSame split as Helm + kubectl. bento owns the declarative state; Portainer owns day-to-day operations.\n\n| Concern | bento | Portainer |\n|---|---|---|\n| Declarative state (what should run, with which envs) | owner | viewer |\n| First deploy + git-backed updates | owner (via API) | executor |\n| Logs, restart, scale, exec | redirect | owner |\n| Stacks created outside bento (no `BENTO_MANAGED` label) | ignored | full owner |\n\nEvery bento-deployed stack carries `BENTO_MANAGED=true` + its source commit, so bento can spot drift and offer to reconcile during **Update**.\n\n---\n\n## Operations\n\n### Update bento and stacks\n\nRe-running the curl|bash command always re-clones the latest `boot.sh`. Or, from the menu, pick **Update** to:\n\n- Pull the latest bento code locally (`git fetch + reset --hard`).\n- Re-deploy any stack whose `compose.yml` or `manifest.json` changed since the last deploy (`POST /api/stacks/\u003cid\u003e/git/redeploy`).\n\n### State and configuration\n\n| Path | Mode | Purpose |\n|---|---|---|\n| `~/.config/bento/state.json` | 600 | Domain, email, IP, generated secrets, deployed-ref per stack |\n| `~/.config/bento/portainer.json` | 600 | Portainer admin credentials |\n| `~/.local/state/bento/logs/` | 700 | Hardening + install logs |\n| `~/.local/share/bento/reports/` | 700 | Handoff HTML reports |\n\nThe state schema is versioned and migrated automatically across bento updates.\n\n### Manual install (no curl|bash)\n\n```bash\ngit clone --branch stable https://github.com/felipefontoura/bento ~/.local/share/bento\ncd ~/.local/share/bento\nbash install.sh\n```\n\n### Requirements\n\n- Latest Ubuntu LTS, Debian, or any apt-based distro\n- `root` or a non-root user with `sudo` — running directly as `root` on a fresh VPS is fine\n- 1+ GB RAM, 5+ GB free disk\n- Public IPv4\n- Wildcard DNS pointing to that IPv4\n\n---\n\n## For maintainers and contributors\n\nAdding a stack, changing conventions, or extending the installer? Read **[CLAUDE.md](CLAUDE.md)** — the canonical maintainer guide. It covers the Bento ↔ Portainer ownership model, the manifest schema, env resolution order, code style for shell + YAML + JSON, and a step-by-step recipe for adding new application stacks (with n8n called out as the gold-standard quality bar).\n\n`.claude/skills/contribute-stack/` is a Claude Code skill (invoked `/contribute-stack`) that automates the new-stack scaffold for AI-assisted contributions. It guards on being inside a repo clone, so it never fires for end users who only installed the `bento` operator plugin.\n\nPRs welcome. Open an issue first for anything beyond a small fix.\n\n---\n\n## License\n\nDistributed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffelipefontoura%2Fbento","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffelipefontoura%2Fbento","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffelipefontoura%2Fbento/lists"}