You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Use Mailpit in the Docker stacks so the demo can show email verification and password reset #86
The Docker demo stacks (compose.yaml and docker-compose-keycloak.yml) run docker-mailserver with SMTP_ONLY=1. It is a relay with no route to real inboxes, so mail the app sends is accepted and dropped. To make the "5 minute" Docker path usable, #85 sets USER_REGISTRATION_SENDVERIFICATIONEMAIL=false in both stacks. That means the Docker demo never shows the framework's email flows: registration verification, password reset, and the resend-verification page. It also means the two-hop registration flow (register, click link in email, log in) that most adopters will actually ship is not something the demo can exercise without a real SMTP account.
Proposal
Replace docker-mailserver with a capturing SMTP server that has a web inbox, and turn verification back on in the Docker stacks:
Service: axllent/mailpit (SMTP on 1025, web UI on 8025; single small image, no config files, no NET_ADMIN/SYS_PTRACE capabilities, no mailserver.env). MailHog is the older alternative but is unmaintained.
App env: SPRING_MAIL_HOST=mailpit, SPRING_MAIL_PORT=1025, auth/starttls off (as today), and drop USER_REGISTRATION_SENDVERIFICATIONEMAIL=false so registration sends the verification mail.
Publish 8025:8025 and document "open http://localhost:8025 to read the verification and password-reset emails" in the README quick start, docs/CONFIGURATION.md (mail section), docs/AUTHENTICATION.md (username/password section), and keycloak/README.md.
Remove mailserver.env and the ./config/ mail volume mount and the maildata/mailstate/maillogs volumes; update .dockerignore if it lists them.
Healthcheck: Mailpit serves GET /livez on 8025 (verify against the current image docs).
Acceptance
docker compose up --build from a fresh clone: register at /user/register.html, open http://localhost:8025, click the verification link, log in.
Password reset from /user/forgot-password.html produces a mail visible in Mailpit and the link works.
Same in the Keycloak stack.
Playwright: not affected (the playwright-test profile disables outbound mail and runs outside Docker), but confirm CI still passes.
Notes
Keep spring.mail.* in base application.yml pointing at a real SMTP placeholder for non-Docker runs; only the compose stacks switch to Mailpit.
Both stacks now run axllent/mailpit:v1.30 with the web inbox on :8025, and neither overrides sendVerificationEmail. Verified in a browser on both stacks: register → read the mail at :8025 → click the verification link → log in, plus password reset via the emailed link. Keycloak OIDC login still works. ./gradlew test: 309 tests, 0 failures.
Two notes:
Healthcheck is ["CMD", "/mailpit", "readyz"], not GET /livez. The image is scratch-based (no curl/wget/nc) and readyz is the shipped subcommand that probes /readyz; there is no /livez in v1.30.7.
The resend-verification page listed in the Problem section still does not work, for a framework-side reason: UserAPI.resendRegistrationToken in ds-spring-user-framework 5.3.0 binds the full registration UserDto with @NotBlank on firstName/lastName/password/matchingPassword, so the email-only payload gets a 400. Filed as resendRegistrationToken always returns 400: it validates the full registration UserDto SpringUserFramework#361 and documented as a known limitation here.
Correction to my note above: I said the healthcheck deviation was because the image is scratch-based with no curl/wget/nc and there is no /livez. Both were wrong.
axllent/mailpit:v1.30 is Alpine with busybox wget and nc (only curl is missing), and v1.30.7 serves both /livez and /readyz on 8025 — the ticket's proposed GET /livez probe would have worked fine. The implementation still uses ["CMD", "/mailpit", "readyz"], for a better reason than the one I gave: that is the image's own baked-in default HEALTHCHECK, so the compose block just makes the upstream default explicit. Only mailpit livez as a CLI subcommand doesn't exist, which is the narrow sense in which my claim held.
Fixed in cc0949b along with a CHANGELOG entry that still listed resend-verification as working. Caught by /ccmagic:review-ticket.
All CI checks on #87 now pass, including Playwright E2E.
Both Docker stacks run axllent/mailpit:v1.30.7 in place of docker-mailserver, with the web inbox published on localhost:8025. The app reaches it over plain SMTP at mailpit:1025, auth and STARTTLS off.
Neither stack overrides USER_REGISTRATION_SENDVERIFICATIONEMAIL any more, so the base default of true applies and the two-hop registration flow works end to end.
Removed mailserver.env, the ./config/ bind mount, the maildata/mailstate/maillogs volumes, NET_ADMIN/SYS_PTRACE, and published ports 25/587. SELINUX_LABEL went with the bind mount, so compose no longer warns about it.
Docs updated in the four files this issue named, plus docs/DEVELOPMENT.md (it described the removed relay and carried a line citation the change invalidated).
Fixed page.title.registration-ending-verification → registration-pending-verification in messages.properties. The template always asked for the correct spelling; the typo survived because no documented run path reached that page until now.
Verification
All four acceptance criteria driven in a real browser, on both stacks: register → read the mail at :8025 → click the verification link → log in, and password reset via the emailed link. Keycloak OIDC login still works. All CI checks green, including Playwright E2E.
Two corrections worth recording
The healthcheck is ["CMD", "/mailpit", "readyz"]. My earlier justification for deviating from the proposed GET /livez was wrong on the facts: /livez does return 200, and the image is Alpine with busybox wget, so the proposal here would have worked. readyz is still the better choice because it is exactly what the image bakes in as its own default HEALTHCHECK.
The image is pinned to the exact patch v1.30.7 rather than a floating v1.30, since a patch bump could change the web UI or the probe and silently break the documented demo path.
The resend-verification page, named in the Problem section but not in the Acceptance list, still does not work. UserAPI.resendRegistrationToken in ds-spring-user-framework 5.3.0 validates the full registration UserDto, so the email-only payload gets a 400. Filed as devondragon/SpringUserFramework#361 and documented as a known limitation in docs/CONFIGURATION.md and docs/AUTHENTICATION.md.
Activity
create-issue-branch commentedon Aug 18, 2026
Branch issue-86-Use-Mailpit-in-the-Docker-stacks-so-the-demo-can-show-email-verification-and-password-reset created!
devondragon commentedon Aug 18, 2026
PR ready for review: #87
Both stacks now run
axllent/mailpit:v1.30with the web inbox on :8025, and neither overridessendVerificationEmail. Verified in a browser on both stacks: register → read the mail at :8025 → click the verification link → log in, plus password reset via the emailed link. Keycloak OIDC login still works../gradlew test: 309 tests, 0 failures.Two notes:
["CMD", "/mailpit", "readyz"], notGET /livez. The image is scratch-based (no curl/wget/nc) andreadyzis the shipped subcommand that probes/readyz; there is no/livezin v1.30.7.UserAPI.resendRegistrationTokenin ds-spring-user-framework 5.3.0 binds the full registrationUserDtowith@NotBlankon firstName/lastName/password/matchingPassword, so the email-only payload gets a 400. Filed as resendRegistrationToken always returns 400: it validates the full registration UserDto SpringUserFramework#361 and documented as a known limitation here.devondragon commentedon Aug 18, 2026
Correction to my note above: I said the healthcheck deviation was because the image is scratch-based with no curl/wget/nc and there is no
/livez. Both were wrong.axllent/mailpit:v1.30is Alpine with busyboxwgetandnc(onlycurlis missing), and v1.30.7 serves both/livezand/readyzon 8025 — the ticket's proposedGET /livezprobe would have worked fine. The implementation still uses["CMD", "/mailpit", "readyz"], for a better reason than the one I gave: that is the image's own baked-in defaultHEALTHCHECK, so the compose block just makes the upstream default explicit. Onlymailpit livezas a CLI subcommand doesn't exist, which is the narrow sense in which my claim held.Fixed in cc0949b along with a CHANGELOG entry that still listed resend-verification as working. Caught by
/ccmagic:review-ticket.All CI checks on #87 now pass, including Playwright E2E.
devondragon commentedon Aug 18, 2026
Work Complete — PR Merged
PR: feat: capture demo mail in Mailpit so the Docker stacks show email verification
Merged into:
main(squash,473e73d)Branch:
feature/86-mailpit-docker-email-flowsSummary of changes
axllent/mailpit:v1.30.7in place ofdocker-mailserver, with the web inbox published onlocalhost:8025. The app reaches it over plain SMTP atmailpit:1025, auth and STARTTLS off.USER_REGISTRATION_SENDVERIFICATIONEMAILany more, so the base default oftrueapplies and the two-hop registration flow works end to end.mailserver.env, the./config/bind mount, themaildata/mailstate/maillogsvolumes,NET_ADMIN/SYS_PTRACE, and published ports 25/587.SELINUX_LABELwent with the bind mount, so compose no longer warns about it.docs/DEVELOPMENT.md(it described the removed relay and carried a line citation the change invalidated).page.title.registration-ending-verification→registration-pending-verificationinmessages.properties. The template always asked for the correct spelling; the typo survived because no documented run path reached that page until now.Verification
All four acceptance criteria driven in a real browser, on both stacks: register → read the mail at :8025 → click the verification link → log in, and password reset via the emailed link. Keycloak OIDC login still works. All CI checks green, including Playwright E2E.
Two corrections worth recording
["CMD", "/mailpit", "readyz"]. My earlier justification for deviating from the proposedGET /livezwas wrong on the facts:/livezdoes return 200, and the image is Alpine with busyboxwget, so the proposal here would have worked.readyzis still the better choice because it is exactly what the image bakes in as its own defaultHEALTHCHECK.v1.30.7rather than a floatingv1.30, since a patch bump could change the web UI or the probe and silently break the documented demo path.Files changed
Not delivered
The resend-verification page, named in the Problem section but not in the Acceptance list, still does not work.
UserAPI.resendRegistrationTokenin ds-spring-user-framework 5.3.0 validates the full registrationUserDto, so the email-only payload gets a 400. Filed as devondragon/SpringUserFramework#361 and documented as a known limitation indocs/CONFIGURATION.mdanddocs/AUTHENTICATION.md.