Skip to content

Use Mailpit in the Docker stacks so the demo can show email verification and password reset #86

Closed

Description

@devondragon

Problem

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

Activity

  1. self-assigned this
    on Aug 18, 2026
  2. devondragon commented on Aug 18, 2026

    @devondragon
    OwnerAuthor

    PR ready for review: #87

    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.
  3. devondragon commented on Aug 18, 2026

    @devondragon
    OwnerAuthor

    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.

  4. devondragon commented on Aug 18, 2026

    @devondragon
    OwnerAuthor

    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-flows

    Summary of changes

    • 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.

    Files changed

    compose.yaml                                    | 61 +++++-----
    docker-compose-keycloak.yml                     | 61 +++++-----
    docs/AUTHENTICATION.md                          | 24 ++++--
    CHANGELOG.md                                    | 24 ++++
    keycloak/README.md                              | 15 ++-
    docs/CONFIGURATION.md                           | 11 +--
    README.md                                       |  9 +--
    docs/DEVELOPMENT.md                             |  9 +--
    mailserver.env                                  | 26 -----
    src/main/resources/messages/messages.properties |  2 +-
    

    Not delivered

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions