Stop Juggling 10 Playbooks! MASH Self-Hosts Everything in One Shot
What if I told you that every self-hosted service you've ever wanted—Matrix, Nextcloud, Vaultwarden, GitLab, PeerTube—could all live on one server, managed by a single command?
Here's the dirty secret nobody talks about: the self-hosting community has been stuck in playbook purgatory for years. Want to run a Matrix server? Clone this repo. Need Nextcloud? That's a different repo. Vaultwarden? Another one. Each with its own quirks, documentation drift, and conflicting dependencies. Before you know it, you're maintaining six different Ansible playbooks like some kind of digital janitor, praying that updating one won't nuke the others.
The nightmare gets worse. Each playbook ships its own Postgres role. Its own Traefik configuration. Its own backup strategy that almost works the same way. You find yourself copy-pasting YAML between projects, manually reconciling directory structures, and explaining to your future self why /opt/matrix and /opt/nextcloud follow completely different conventions.
Enter MASH—the Mother-of-All-Self-Hosting Ansible playbook. This isn't just another tool. It's the nuclear option against playbook sprawl. One repository. One mental model. Fifty-plus services. All running as clean, predictable Docker containers across any supported distro or CPU architecture. The team behind this literally absorbed their previously separate playbooks into this monolith because they were tired of their own maintenance hell.
If you're serious about self-hosting without losing your sanity, keep reading. This is the workflow you've been waiting for.
What Is the MASH Playbook?
MASH (Mother-of-All-Self-Hosting) is an Ansible playbook developed by the team at devture.com that automates the deployment of free and open-source software (FOSS) services as Docker containers on your own infrastructure.
The project emerged from a very real pain point: its creators maintained separate playbooks for Matrix, Nextcloud, Gitea, GitLab, Vaultwarden, PeerTube, and more. Each was well-crafted individually, but collectively they became a maintenance catastrophe. Shared components like Postgres databases, Traefik reverse proxies, and backup solutions were duplicated across repositories, drifting out of sync with every update.
Why it's trending now: The self-hosting renaissance is accelerating. Developers and privacy-conscious users are fleeing SaaS lock-in, but the tooling hasn't kept pace. MASH arrives as a consolidated alternative that doesn't sacrifice quality for convenience. It inherits the battle-tested reliability of its predecessor playbooks—some of which served thousands of production deployments—while eliminating the fragmentation that made advanced self-hosting feel like a second job.
The name itself tells the story. Beyond the catchy acronym, "mashing" references both the brewing process (where combining ingredients creates something greater) and the act of mixing software components into your ideal stack. The playbook doesn't force you to host everything on one server—you can spread services across machines—but it does give you one unified control plane for whatever architecture you choose.
Critically, MASH isn't a toy project. The Matrix Docker Ansible Deploy playbook—arguably the most mature Matrix deployment tool in existence—shares DNA with this project. The team deliberately kept Matrix separate due to its complexity, but everything else? Absorbed. Battle-tested. Ready to deploy.
Key Features That Eliminate Self-Hosting Friction
MASH isn't just a bundle of roles thrown together. It's an architectural philosophy designed to make sophisticated infrastructure feel effortless.
🐋 Docker-Native by Design
Every service runs in containers, giving you immutable deployments, clean rollbacks, and environment parity. No more "works on my machine" when your production server runs a different OS version. The playbook handles image pulls, network creation, volume management, and container orchestration without you touching docker-compose.yml manually.
🔄 Predictable, Up-to-Date Deployments Because containers encapsulate dependencies, MASH delivers identical environments across Ubuntu, Debian, Fedora, and their derivatives. ARM64? x86_64? The playbook abstracts architecture differences so you can deploy on a Raspberry Pi or a beefy VPS with the same variables file.
📦 Shared Infrastructure, Zero Duplication
This is where MASH destroys the competition. One Postgres instance serves multiple applications. One Traefik configuration routes traffic everywhere. One backup strategy covers your entire stack. The base data path is unified (/mash by default), so you know exactly where to look when troubleshooting.
🚀 One-Command Service Addition
Want to try Miniflux? Add four lines to your vars.yml, run the playbook, and it's live. No new repository to clone. No conflicting role versions. No wondering if this playbook's Postgres role is compatible with your existing one. The miniflux role lives in the same ecosystem, maintained by the same team, tested against the same dependencies.
🛡️ Production-Hardened Defaults Security isn't bolted on—it's baked in. The playbook inherits patterns from deployments serving thousands of users: proper secret management, least-privilege container configurations, automated TLS via Let's Encrypt, and sensible firewall rules through Traefik.
📋 Automated Lifecycle Management Installation, upgrades, and maintenance tasks are all Ansible-native. Need to rotate credentials? Update a variable, re-run the playbook. Scaling up? The idempotent design means running it twice won't break anything. The changelog tracks breaking changes so updates are predictable, not terrifying.
Real-World Scenarios Where MASH Dominates
Scenario 1: The Privacy-Focused Professional
You're a consultant who needs Vaultwarden (password management), Nextcloud (file sync), and Miniflux (RSS aggregation). Previously, that's three playbooks, three Postgres instances eating RAM, and three backup scripts. With MASH? One vars.yml, one Postgres container, one ansible-playbook invocation. Your $10 VPS suddenly runs like a $40 one because resources aren't squandered on duplication.
Scenario 2: The Indie Media Creator
You want PeerTube for video hosting, Gitea for code, and a Matrix server for community chat. Wait—Matrix stays separate, but everything else? MASH handles PeerTube and Gitea with shared Traefik routing and unified SSL management. Your audience accesses peertube.yourdomain.com and git.yourdomain.com through the same reverse proxy configuration, with consistent security headers and rate limiting.
Scenario 3: The Homelab Experimenter
You spin up services weekly, testing what's worth keeping. Yesterday it was Wiki.js; today, Plausible analytics. With discrete playbooks, each experiment leaves cruft—dangling networks, forgotten volumes, role repositories you never delete. MASH's unified structure means cleanup is mash_service_wikijs_enabled: false and a re-run. The container disappears; the data volume persists (or doesn't—your choice); your system stays pristine.
Scenario 4: The Small Team Escapee from SaaS
Your startup's Notion, Figma, and Slack bills are crushing you. You deploy Outline (wiki), Penpot (design), and Mattermost (chat) through MASH. The playbook's shared backup solution—configured once—snapshots everything to S3-compatible storage. When your cloud provider hiccups, you restore from the same base path using the same procedures, because everything lives together.
Step-by-Step Installation & Setup Guide
Ready to escape playbook hell? Here's your escape route.
Prerequisites
You'll need:
- A server (VPS, bare metal, or even a home machine with port forwarding)
- A domain name with DNS control
- SSH key access to your server as a non-root user with sudo privileges
- Ansible 2.14+ on your local machine (the control node)
Step 1: Clone the Repository
# Grab the playbook that replaces your entire self-hosting toolchain
git clone https://github.com/mother-of-all-self-hosting/mash-playbook.git
cd mash-playbook
Step 2: Install Ansible Dependencies
# MASH uses ansible-galaxy roles; install them automatically
ansible-galaxy install -r requirements.yml
Step 3: Configure Your Inventory
Create inventory/hosts to tell Ansible where your server lives:
[mash_servers]
your-server.com ansible_host=203.0.113.10 ansible_user=admin
[mash_servers:vars]
ansible_python_interpreter=/usr/bin/python3
Step 4: Create Your Variables File
This is where the magic happens. Create inventory/host_vars/your-server.com/vars.yml:
---
# ═══════════════════════════════════════════════════
# MASH Playbook Configuration
# ═══════════════════════════════════════════════════
# The base domain for all your services
mash_domain: "yourdomain.com"
# Where all service data lives on disk
mash_playbook_base_path: "/mash"
# Enable the services you actually want
mash_service_vaultwarden_enabled: true
mash_service_nextcloud_enabled: true
mash_service_miniflux_enabled: true
mash_service_traefik_enabled: true
mash_service_postgres_enabled: true
# Traefik configuration for automatic HTTPS
mash_service_traefik_config_certificatesResolvers_acme_email: "you@yourdomain.com"
# Postgres superuser password (use ansible-vault in production!)
mash_service_postgres_superuser_password: "your-very-secure-password-here"
# Vaultwarden admin token
mash_service_vaultwarden_config_admin_token: "another-secure-token"
Critical: Use ansible-vault for secrets in production. Never commit plaintext passwords.
Step 5: Deploy Everything
# The single command that replaces hours of manual setup
ansible-playbook -i inventory/hosts setup.yml --become
The --become flag escalates to root for Docker installation and system-level configuration. The playbook is idempotent—run it again after adding services, and only changes are applied.
Step 6: Verify and Access
Once complete, your services are live at:
https://vaultwarden.yourdomain.comhttps://nextcloud.yourdomain.comhttps://miniflux.yourdomain.com
Traefik automatically handles Let's Encrypt certificates. No manual certbot dances required.
REAL Code Examples: Inside MASH's Architecture
Let's dissect actual patterns from the MASH ecosystem to understand how this playbook achieves its magic.
Example 1: Service Enablement Pattern
This is the core UX innovation—enabling a service requires minimal configuration:
# inventory/host_vars/your-server.com/vars.yml
# ═══════════════════════════════════════════════════
# Enabling services in MASH: add two lines, deploy, done.
# ═══════════════════════════════════════════════════
# Shared infrastructure (enabled once, used by many)
mash_service_traefik_enabled: true # Reverse proxy + SSL termination
mash_service_postgres_enabled: true # Shared database engine
mash_service_backup_enabled: true # Unified backup strategy
# Application services (mix and match as needed)
mash_service_vaultwarden_enabled: true # Password manager
mash_service_nextcloud_enabled: true # File sync & collaboration
mash_service_miniflux_enabled: true # RSS reader
mash_service_gitea_enabled: false # Disabled—no resources wasted
Why this matters: Each boolean triggers a conditional include in the main playbook. Disabled services consume zero container resources, zero network ports, zero mental overhead. The mash_service_*_enabled naming convention is consistent across all 50+ services, so you never hunt for the right variable name.
Example 2: Traefik Integration Pattern
Here's how MASH achieves automatic HTTPS without per-service configuration:
# Simplified from Traefik role defaults—shows the architectural pattern
# ═══════════════════════════════════════════════════
# Traefik acts as the single entrypoint for ALL services
# ═══════════════════════════════════════════════════
mash_service_traefik_config_entrypoints:
web:
address: ":80"
# Redirect all HTTP to HTTPS automatically
http:
redirections:
entryPoint:
to: websecure
scheme: https
permanent: true
websecure:
address: ":443"
# TLS configuration applied globally
http:
tls:
certResolver: default
certificatesResolvers:
default:
acme:
email: "{{ mash_service_traefik_config_certificatesResolvers_acme_email }}"
storage: "/letsencrypt/acme.json"
tlsChallenge: {} # Automatic Let's Encrypt validation
The insight: Individual services don't configure TLS. They declare traefik.enable=true and traefik.http.routers.*.tls=true labels on their containers. Traefik—already running with full certificate management—discovers them via Docker socket and handles everything. This inversion of control eliminates per-service certificate management forever.
Example 3: Postgres Shared Database Provisioning
MASH's shared Postgres is a resource miracle. Here's how services consume it:
# From a service role (e.g., Vaultwarden) that needs database access
# ═══════════════════════════════════════════════════
# Services don't run their own Postgres—they request a database
# ═══════════════════════════════════════════════════
# This role executes ONLY when the parent service is enabled
- name: Ensure Vaultwarden Postgres database exists
when: mash_service_vaultwarden_enabled | bool
block:
- name: Create database for Vaultwarden
community.postgresql.postgresql_db:
name: "vaultwarden"
state: present
# Connects to the shared Postgres container via internal Docker network
become_user: "postgres"
- name: Create dedicated user with limited privileges
community.postgresql.postgresql_user:
name: "vaultwarden"
password: "{{ mash_service_vaultwarden_config_database_password }}"
db: "vaultwarden"
priv: ALL
state: present
The architecture: One Postgres container, multiple databases, least-privilege users. Memory footprint: ~200MB once, not 200MB × N services. Backup strategy: pg_dumpall from one host, not N different backup scripts. The when: mash_service_vaultwarden_enabled | bool guard ensures this only runs if you've actually requested Vaultwarden.
Example 4: Idempotent Deployment Command
The playbook's entry point demonstrates Ansible best practices:
# setup.yml - the main playbook orchestrating everything
# ═══════════════════════════════════════════════════
# Run this after ANY configuration change—it's safe to repeat
# ═══════════════════════════════════════════════════
# Full deployment with privilege escalation
ansible-playbook -i inventory/hosts setup.yml --become --ask-become-pass
# Or with vault-encrypted secrets
ansible-playbook -i inventory/hosts setup.yml --become --ask-vault-pass
# Limit to specific tags for faster iteration during development
ansible-playbook -i inventory/hosts setup.yml --tags=setup-vaultwarden,setup-traefik
Key flags explained:
--become: Escalates to root for Docker socket access and package installation--ask-become-pass: Prompts for sudo password (alternative to passwordless sudo)--ask-vault-pass: Decryptsansible-vaultprotected secrets interactively--tags: Targets specific roles for rapid iteration without full playbook runs
Advanced Usage & Best Practices
🔐 Vault Your Secrets Immediately The example configs show plaintext passwords for clarity. In production, encrypt with:
ansible-vault encrypt inventory/host_vars/your-server.com/secrets.yml
Reference vaulted variables with {{ vault_postgres_password }} and use --ask-vault-pass at runtime.
🗂️ Separate Servers by Criticality MASH enables monolithic deployment, but don't host your password manager and experimental RSS reader on the same box. Use inventory groups to segment:
[mash_production]
primary.yourdomain.com
[mash_experimental]
playground.yourdomain.com
📊 Monitor Shared Resource Contention
One Postgres serving ten databases is efficient until one misbehaves. Enable Postgres metrics collection and set mash_service_postgres_config_max_connections appropriately. The playbook exposes these tunables without requiring manual postgresql.conf editing.
🔄 Test Upgrades in Staging The changelog flags breaking changes, but verify yourself:
# Dry-run to preview changes without applying
ansible-playbook -i inventory/hosts setup.yml --check --diff
🧹 Prune Disabled Services
Setting enabled: false stops containers but may leave volumes. For complete removal, add the dedicated teardown tag (check role documentation for availability) or manually prune:
docker volume rm mash-vaultwarden-data # Only after confirming backups!
Comparison with Alternatives
| Feature | MASH Playbook | Individual Playbooks | Docker Compose Stacks | Kubernetes Helm Charts |
|---|---|---|---|---|
| Unified Infrastructure | ✅ Single Postgres, Traefik, backups | ❌ Duplicated per service | ⚠️ Manual sharing | ✅ Via operators |
| Service Discovery | ✅ Automatic via Ansible inventory | ❌ Manual coordination | ❌ Hardcoded networks | ✅ Native |
| Learning Curve | 📉 One mental model | 📈 N different conventions | 📉 Simple initially | 📈 Steep |
| Production Hardening | ✅ Inherited from 1000+ deployments | ⚠️ Varies by project | ❌ DIY security | ✅ Enterprise-grade |
| Resource Efficiency | ✅ Shared containers | ❌ Isolated overhead | ⚠️ Configurable | ❌ Control plane tax |
| ARM64/Raspberry Pi | ✅ First-class support | ⚠️ Spotty | ⚠️ Image-dependent | ❌ Often absent |
| Upgrade Experience | ✅ Single changelog, idempotent | ❌ Track N repositories | ⚠️ Manual image bumps | ✅ Rolling updates |
| Small Service Support | ✅ Miniflux, etc. find a home | ❌ No dedicated playbook | ✅ Any image | ✅ Any image |
The verdict: Kubernetes wins at hyperscale but drowns solo operators in complexity. Docker Compose is simple until you need SSL, backups, and multi-host. Individual playbooks work until you're maintaining a garden of repositories that never sync. MASH occupies the pragmatic middle—orchestrated enough for production, simple enough for weekend projects.
FAQ
Q: Can I migrate from the separate Matrix/Nextcloud/Vaultwarden playbooks to MASH?
Yes, though it requires planning. The MASH team absorbed their own playbooks here, so migration paths exist. Back up data, align directory structures to /mash, and transfer secrets. Join the Matrix support room for guidance.
Q: Does MASH replace the dedicated Matrix playbook? No—the Matrix playbook remains separate due to its complexity. MASH focuses on everything else. You can run both on different servers, or integrate them with manual Traefik configuration if adventurous.
Q: How do I add a service that isn't officially supported?
MASH uses standard Ansible roles. You can develop custom roles following the project's patterns and include them via requirements.yml. Alternatively, request additions via GitHub issues—the maintainers actively expand coverage.
Q: Is this suitable for production with hundreds of users? Absolutely. The architecture inherits from deployments serving thousands. For high availability, spread services across multiple MASH-managed servers rather than piling everything onto one box.
Q: What about backup and disaster recovery?
MASH unifies data paths under /mash and provides integrated backup roles. Configure your destination (local, S3, etc.) once, and all services inherit coverage. Restore by re-running the playbook against fresh infrastructure with your data volumes.
Q: Can I run MASH without Docker? No—containerization is foundational to MASH's predictability. The playbook installs and configures Docker automatically, so you don't manage it manually, but Docker is non-negotiable.
Q: How often is MASH updated?
Actively. The changelog tracks all changes, and the team maintains compatibility with latest upstream releases. Update by pulling the repository and re-running setup.yml.
Conclusion
The self-hosting landscape has been fragmented for too long. We've accepted playbook sprawl as inevitable, tolerated duplicated infrastructure as "just how things are," and wasted countless hours reconciling divergent conventions across projects that should work together.
MASH says: enough.
This isn't about laziness—it's about focus. Every hour spent maintaining five Postgres instances is an hour not spent improving your actual services. Every mental context switch between playbook repositories is cognitive load that could go toward building something meaningful.
The team behind MASH literally ate their own dog food, consolidating playbooks they'd spent years perfecting because the overhead became unbearable even for them. That should tell you everything about whether this problem is real.
If you're self-hosting more than two services, or planning to, MASH isn't just an option—it's the baseline you should compare everything else against. Clone it. Configure it. Run one command. Then watch your server populate with exactly what you asked for, nothing more, nothing less, all speaking the same language.
Your future self—the one not debugging conflicting Traefik versions at 2 AM—will thank you.
👉 Get MASH on GitHub — star it, try it, and join the Matrix community when you're ready to go deeper.