A step-by-step guide to running a multi-location clinic chain on self-hosted VBWD: import the clinic demo, model each branch, set up multi-practitioner booking, patient portals, role-based access and per-clinic billing.
A practical, step-by-step guide.
This guide walks a systems integrator or clinic IT lead through configuring VBWD to run a multi-location medical practice — several clinics, many practitioners, shared patient records, and separate billing — on infrastructure you control.
What you get, and what you don’t
VBWD is a self-hosted, source-available platform: Python/Flask and PostgreSQL on the backend, Vue 3 on the frontend, packaged with Docker. It is built as an agnostic core with a plugin layer, so clinic-specific behaviour (booking, patient portals, messaging) lives in plugins rather than forks of the core. That distinction matters for a clinic chain because you can upgrade the platform without losing your customisations.
Be honest with yourself about scope before you start. Running VBWD on EU-resident servers gives you data residency by architecture — the patient data sits on machines in a jurisdiction you choose. That is not the same as HIPAA certification or EHR certification, and nothing in this guide grants it. Lawful basis for processing, a Data Protection Impact Assessment, retention schedules, and staff training remain your responsibility as the operator. The chat plugin described below is general secure communication, not clinical decision support. Treat it as a channel for appointment coordination, not a place to store diagnoses.
Prerequisites
- A Linux host (or several) with Docker and Docker Compose installed. For a small chain, one machine with 4 vCPU and 8 GB RAM is a reasonable starting point.
- Shell access and a domain name per public-facing surface (for example
booking.yourgroup.euand one CMS hostname per clinic). - PostgreSQL 16 and Redis 7 — both are provisioned by the bundled Docker setup, so you don’t install them separately.
- Basic familiarity with editing
.envfiles and runningmaketargets. - A decision, made up front, on your billing model: one invoice stream for the whole group, or a separate stream per clinic. Step 6 depends on this.
On licensing: VBWD ships under BSL 1.1 (Business Source Licence). Commercial use is free while annual VBWD-attributable sales stay below the value of 6.7 BTC per year. Above that threshold you need a commercial licence. It is source-available, not public domain — do not treat it as CC0. Read the LICENSE file in the repository before you deploy for a paying group.
Step 1 — Install the base platform
Clone the SDK and run the one-command installer. It clones the three frontend repositories, wires the backend, and brings up the containers.
git clone --recurse-submodules https://github.com/vbwd/vbwd-sdk.git
cd vbwd-sdk
./recipes/dev-install-ce.sh
When the script finishes, the user app answers on port 8080 and the admin backoffice on port 8081. Confirm both respond before continuing:
curl -sf http://localhost:8080 >/dev/null && echo "user app up"
curl -sf http://localhost:8081 >/dev/null && echo "admin app up"
A returned HTTP 200 tells you the web tier is serving. It does not by itself tell you that every plugin loaded — you verify that in Step 8.
Step 2 — Import the doctor/clinic demo
Rather than model a clinic from an empty database, import the doctor/clinic demo dataset. It seeds practitioner profiles, appointment types, and sample locations so you have a working shape to adapt instead of a blank form. Browse the available building blocks on the plugins catalogue and enable the ones your chain needs — at minimum booking, patient accounts, and messaging.
Import from the admin app under the plugin’s import panel, or run the plugin’s populate script inside the API container. After import, spot-check that the demo practitioners and appointment types appear in the admin under the booking section. Delete the sample data only after you have confirmed your own real records import cleanly.
Step 3 — Model each clinic as a location
The organising principle for a chain is one location (branch) record per physical clinic. Every downstream entity — a practitioner’s availability, a room, an invoice, a CMS page — attaches to a location. Create your locations first:
- One location record per clinic, with its address, timezone, and opening hours.
- A short, stable slug per clinic (for example
clinic-berlin-mitte) — you will reuse it in RBAC scoping and CMS routing. - A default currency and tax rate per location if clinics operate across borders.
Getting the location model right at this stage saves rework later, because practitioners, schedules, and billing all reference it. The platform’s architecture overview explains how the agnostic core keeps these location-scoped entities separate from plugin logic, which is why adding a clinic later is a data operation rather than a code change.
Step 4 — Configure multi-practitioner booking
Enable the booking plugin and attach practitioners to locations. A practitioner can belong to more than one clinic; their availability is defined per location so a doctor working Mondays in Berlin and Wednesdays in Munich is one record with two schedules, not two accounts.
For each practitioner set:
- Availability windows per location (day, start, end).
- Appointment types they offer and each type’s duration and buffer.
- Concurrency — whether a slot books one patient or a group.
The booking engine derives free slots from availability minus existing appointments, so front-desk staff and patients see only genuinely open times. Details of the scheduling and availability model are documented under the platform features section; read it before you hand-tune buffers, because the default buffer behaviour prevents double-booking across appointment types at the same location.
Step 5 — Set up patient accounts and portals
Patients get self-service accounts through the patient portal plugin. From the portal a patient can register, see their upcoming appointments across any clinic in the group, and manage their own bookings. Because accounts are group-wide rather than per-clinic, a patient who visits two of your locations has a single login and one appointment history.
Configure the registration flow to collect only the fields you have a lawful basis to hold. This is where your DPIA feeds directly into configuration: if you do not need a field, do not add it to the registration form. Fewer fields is both a compliance posture and less data to secure.
Step 6 — Apply granular RBAC for the three staff types
A clinic chain has at least three distinct staff roles, and VBWD’s role model separates them cleanly. It has three cooperating role systems: a top-level account role, admin-area roles that gate the backoffice, and access levels that gate the user-facing side. Map your staff onto them:
- Front-desk — can view and manage appointments and patient contact details for their own clinic, but not clinical notes or group-wide billing.
- Practitioner — can see their own schedule and their patients, across the locations they work at.
- Group-admin — can manage locations, plugins, staff accounts, and billing for the whole chain.
Scope each role to a location slug so a front-desk account created for Berlin cannot read Munich’s patient list. Test this explicitly: log in as a scoped front-desk account and confirm that another clinic’s data returns nothing rather than an empty-but-present page. RBAC misconfiguration is the most common way a multi-tenant clinic setup leaks data, so treat this step as a security checkpoint, not a formality.
Step 7 — Choose shared or per-clinic billing
Now apply the billing decision you made in the prerequisites. Two workable models:
- Shared billing — one invoice stream and one set of tax settings for the group. Simplest to operate; suits chains under a single legal entity.
- Per-clinic billing — each location has its own invoice sequence, currency, and tax rate. Necessary when clinics are separate legal entities or operate in different countries.
Set the invoicing configuration at the location level for per-clinic billing, or at the group level for shared billing. Subscriptions and one-off invoices both respect the location scope, so a patient’s invoice carries the correct clinic’s details and tax rate. If you run cross-border, verify that each location’s currency and tax rate produce a correctly rounded total on a test invoice before going live.
Step 8 — Add secure patient-clinic messaging and per-clinic CMS pages
Enable the chat plugin to give patients a channel to message their clinic — appointment questions, rescheduling, document requests. Keep the scope honest: it is secure general communication, not a clinical record. Do not route diagnoses or treatment decisions through it.
Finally, give each clinic its own public presence with the CMS. Create one set of CMS pages per location — address, opening hours, the practitioners who work there, and directions. Because CMS content is location-scoped, editing Berlin’s opening hours never touches Munich’s page. The full page-type and routing model is covered in the documentation.
Verification
Before you open the system to real patients, walk this checklist end to end:
- Create a test patient, book an appointment at Clinic A, and confirm it appears in that patient’s portal.
- Log in as a front-desk account scoped to Clinic B and confirm you cannot see the Clinic A appointment.
- Log in as a practitioner who works at both clinics and confirm both schedules are visible.
- Generate a test invoice at each clinic and check the currency, tax rate, and clinic details are correct.
- Send a test message through the chat plugin and confirm delivery in both directions.
- Load each clinic’s public CMS page and confirm the right address and practitioner list render.
If any step returns the wrong clinic’s data, stop and revisit Step 3 and Step 6 — the fault is almost always a location scope that was left blank.
Limits and honesty
Self-hosting on EU-resident infrastructure gives you data residency and full control of the stack. It does not give you medical-software certification. VBWD is a scheduling, portal, billing, and communication platform — not a certified EHR and not HIPAA-certified. Your obligations as operator remain: establish a lawful basis for each category of data, complete and maintain a DPIA, define and enforce retention periods, and train staff on handling patient data. The messenger is a general secure channel, not clinical decision support. Configuration handles the technical side; compliance is organisational work that no software choice removes.
Takeaway
The whole configuration reduces to a small number of decisions made in order: install the base, import the demo to get a working shape, model each clinic as a location, then attach booking, patient accounts, RBAC, billing, messaging, and CMS pages to those locations. Get the location model and the RBAC scoping right and the rest follows, because every plugin reads the same location boundary. Because VBWD keeps the core agnostic and the clinic behaviour in plugins, adding the fourth or fifth clinic later is a data task, not a rebuild.
Learn more about VBWD
VBWD is a self-hosted, source-available platform for building subscription products, marketplaces, and AI-powered apps. Explore it further:
- 🌐 Website and documentation: vbwd.cc — see the plugins, architecture, and developer docs.
- 💻 Source code and plugins on GitHub: github.com/VBWD-platform
- 🎥 Watch VBWD in action: demo video 1 and demo video 2
- 💼 Follow the project on LinkedIn: linkedin.com/company/vbwd
VBWD is source-available — get the SDK on GitHub.