Quick start
Run AppointMe locally. Install the .NET 10 template from NuGet or clone the repo, trust the dev certificate, then start the stack with .NET Aspire or Docker Compose.
- You'll get
- The full stack running on
localhost - Time
- Minutes, plus the first image pull
- Touches
- Nothing: the committed defaults run as is
- Before you start
- .NET 10 SDK, Docker, Node 24
Prerequisites
Section titled “Prerequisites”Install three tools and trust one certificate. The versions come from the repo’s own pins.
| Requirement | Version | Pinned in | Check with |
|---|---|---|---|
| .NET SDK | 10.0.0 or later, no previews (rollForward: latestMajor) |
global.json |
dotnet --version |
| Docker | Running. Docker Desktop or another OCI-compatible runtime. | — | docker info |
| Node.js | 24 recommended. The engines field accepts ^22.18.0 || ^24.11.0 || >=26.0.0. |
src/AppointMe.Frontend/.node-version, package.json |
node --version |
| HTTPS dev certificate | Trusted, once per machine | Checked by src/AppointMe.Aspire/DevCertificate.cs |
dotnet dev-certs https --check --trust |
The first run pulls three container images and restores the NuGet and npm packages, so it takes a few minutes. Later runs reuse the containers.
Run it
Section titled “Run it”Three paths: generate a renamed copy from NuGet and run Aspire, clone and run Aspire, or clone and run Docker Compose.
-
Get the code. Generate a renamed copy from the NuGet template, or clone the repo as it is.
Terminal window dotnet new install BravoDev.AppointMe.Templates::1.2.1dotnet new appointme -n Contoso.Bookingcd Contoso.BookingTerminal window git clone https://github.com/bravodev-hub/appointme.gitcd appointme-nmust be a dotted .NET identifier such asContoso.Booking, because it becomes namespaces, project names and folders. A name likemy-booking-appgenerates a solution that doesn’t compile, anddotnet newstill reports success. In a generated project, readAppointMein the paths below as your name, for examplesrc/Contoso.Booking.Aspire. -
Trust the HTTPS dev certificate. The API, the frontend and Keycloak all serve HTTPS locally.
Terminal window dotnet dev-certs https --trustIf it’s still reported as untrusted (for example, a certificate created by an older SDK), run
dotnet dev-certs https --cleanand trust it again. -
Start the stack. Aspire runs everything. Compose runs only the backing services, so you start the API and the frontend yourself.
Terminal window cd src/AppointMe.Aspiredotnet runOr open
AppointMe.slnin Visual Studio or Rider, setAppointMe.Aspireas the startup project and press F5. The Aspire dashboard opens in your browser. Wait until every resource shows Running.Terminal window ./docker/keycloak/export-dev-cert.shdocker compose up -ddocker compose psdotnet run --project src/AppointMe.Api --launch-profile httpsThe script trusts the dev certificate and exports it to
docker/keycloak/certs/for Keycloak to serve. Without bash, run the export it wraps:dotnet dev-certs https --format PEM --no-password -ep docker/keycloak/certs/keycloak.crt. Start the API oncedocker compose psshows SQL Server and Keycloak ashealthy.Then, in a second terminal:
Terminal window cd src/AppointMe.Frontendnpm cinpm run dev -
Open the app. Go to
https://localhost:5173, then create an account or use the demo login.
What happens on startup
Section titled “What happens on startup”Aspire starts each resource after the ones it depends on. The API applies every module’s migrations as it starts.
npm install --no-save, then Vite on 5173| Step | Done by | Where |
|---|---|---|
| Checks the HTTPS dev certificate. Exits if it’s untrusted, or only warns on Linux. | AppHost | src/AppointMe.Aspire/DevCertificate.cs |
| Starts SQL Server, Keycloak and Mailpit as persistent containers, which later runs reuse | AppHost | src/AppointMe.Aspire/Program.cs |
Imports the appointme realm: clients, roles, the demo user and SMTP to Mailpit |
Keycloak | src/AppointMe.Aspire/appointme-realm.json |
| Applies EF Core migrations for Identity, Organizations, CRM and Booking, each in its own schema | API | src/AppointMe.Shared/Database/Migrations/DatabaseMigrations.cs |
Creates its message tables (wolverine_*, in dbo at this commit) and its job tables in the hangfire schema |
Wolverine, Hangfire | src/AppointMe.Api/Wolverine, src/AppointMe.Api/Hangfire |
Installs npm packages without rewriting package-lock.json, then starts the dev server |
AppHost | AddViteApp(...) in Program.cs |
There’s no demo data yet at this point. It’s created per company: when a company is registered and Demo:Enabled is on, DemoSeedingSaga seeds 150 customers and about 450 appointments for it. Under Compose, the API steps run when you start the API.
Where everything lives
Section titled “Where everything lives”The app is on https://localhost:5173. Every credential below is a committed local default.
| What | URL | Sign in with |
|---|---|---|
| The app | https://localhost:5173 |
An account you sign up, or the demo login |
| Demo login | https://localhost:5173/api/v1/login/demo |
Nothing: it signs in demo@appointme.dev |
| API | https://localhost:7233 (also http://localhost:5179) |
The app’s cookie, or a bearer token |
| OpenAPI document | https://localhost:7233/openapi/v1.json |
Anonymous, Development only |
| Hangfire dashboard | https://localhost:5173/admin/jobs |
A super admin, which is demo@appointme.dev locally |
| Aspire dashboard | https://localhost:17082 (Aspire only) |
The URL the AppHost prints in its console |
| Keycloak admin console | https://localhost:8082 |
admin / admin |
| Mailpit | http://localhost:8026 |
— |
| SQL Server | localhost,60740 |
sa / Password1 |
The demo user’s password is AppointMe1, if you’d rather sign in on the Keycloak page. Local services & ports lists every image, port and volume, and What to change first covers why you must never reuse these values.
Create your first account
Section titled “Create your first account”Sign-up runs through the app, and Keycloak’s verification email lands in Mailpit.
-
Sign up. Open
https://localhost:5173/auth/signupand enter your name and email. There’s no password field: the API creates the user in Keycloak with two required actions,VERIFY_EMAILandUPDATE_PASSWORD, and asks Keycloak to send the verification email. -
Open the email in Mailpit. Go to
http://localhost:8026. The message comes fromnoreply@appointme.local, which Keycloak sends to the SMTP hostMailpit. -
Follow the link. Keycloak verifies the address and has you set a password. The link then returns you to the app’s sign-in page with
returnUrl=/onboarding. -
Create your company. Onboarding registers your first company. With
Demo:Enabledon, it gets the seeded customers and appointments, and the dashboard has data to show.
Skip sign-up: the demo login
Section titled “Skip sign-up: the demo login”https://localhost:5173/api/v1/login/demo signs you in as the seeded demo user, with no form and no email.
The endpoint signs the Demo:User credentials in to Keycloak with a password grant, sets the app’s auth cookie and redirects to Frontend:BaseUrl. It’s the same flow as the public live demo. From there, complete onboarding to create a company.
"Demo": { "Enabled": true, "User": { "Email": "demo@appointme.dev", "Password": "AppointMe1", "Name": "John Doe" }}appsettings.json sets Demo:Enabled to false, so outside Development the endpoint answers 404:
if (!demoOptions.Value.Enabled || demoOptions.Value.User is not { } demoUser){ return TypedResults.NotFound();}In Development the demo user is also listed in Authentication:SuperAdmins, which is what opens the Hangfire dashboard.
Troubleshooting
Section titled “Troubleshooting”Most first-run failures are the certificate, a port, or the API launch profile. The full list is in Troubleshooting.
| Symptom | Cause | Fix |
|---|---|---|
The AppHost exits with error: the ASP.NET Core HTTPS development certificate is missing or not trusted. |
The dev certificate is missing or untrusted | dotnet dev-certs https --trust. If that doesn’t help, --clean first, then --trust. |
appointme-api stays in Waiting |
It waits for SQL Server, Keycloak and Mailpit, and the first run is still pulling images | Wait, or open each container’s logs in the Aspire dashboard |
Bind for 0.0.0.0:60740 failed: port is already allocated |
Aspire and Compose use the same host ports, and Aspire’s containers keep running after you stop the AppHost | Stop one set: docker compose stop, or stop the Aspire containers in Docker |
| Under Compose, the app can’t reach the API | The API was started without --launch-profile https |
Restart it with --launch-profile https |
Under Compose, Keycloak never turns healthy |
docker/keycloak/certs/keycloak.crt and keycloak.key don’t exist yet |
Run ./docker/keycloak/export-dev-cert.sh, then docker compose up -d --force-recreate keycloak |
/api/v1/login/demo returns 404 |
Demo:Enabled is false, or there’s no Demo:User |
Set both in appsettings.Development.json |
| The generated solution doesn’t compile | -n wasn’t a dotted identifier |
Generate again with a name like Contoso.Booking |
Why does the AppHost refuse to start without a trusted certificate?1 min · optional
Without a trusted dev certificate, the API’s https launch profile can’t start, and Aspire quietly serves Keycloak over plain HTTP. Nothing fails at startup. The first sign of trouble is an unexplained 500 from the login endpoint, much later.
Running dotnet dev-certs https --check --trust up front turns that into an error that names the fix. Linux has no single trust store the tool can check, so a failure there is often a false negative, and the AppHost only warns.