# DuckMail: a Next.js front end for temporary and hosted mailboxes

> DuckMail is a TypeScript web client for temporary addresses and hosted Microsoft mailboxes, built on Next.js, React and HeroUI. It ships the interface and an API proxy, keeps session state in the browser, and locks its own routes against direct script access.

**MoonWeSif/DuckMail** — 优雅易用的临时邮箱客户端，支持 API 调用、接入私有域名与托管 Microsoft 邮箱

- Repository: https://github.com/MoonWeSif/DuckMail
- Website: https://www.duckmail.sbs/
- Stars: 864 · Forks: 187
- Language: TypeScript
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/moonwesif-duckmail

## DuckMail separates the interface from the mail backend on purpose

The architecture is stated in one line near the top of the README and it determines everything else about the project: the repository holds the web interface and an API proxy, while mail reception and storage are provided by a separate backend. By default that backend is the DuckMail API, and the settings screen can switch to Mail.tm or to another service that speaks the same protocol.

So DuckMail is not a mail server. There is no SMTP listener, no message store and no mailbox database in this codebase. What is here is a TypeScript application built on Next.js, React and HeroUI, with the usual Next.js layout visible in the tree: an app directory, components, contexts, hooks, i18n, messages, styles, lib, types and tests, plus a middleware.ts at the root for request interception.

That split is the main thing to understand before reading any feature list. Temporary address generation, mailbox listing, inbox refresh and attachment downloads all depend on an upstream API being reachable and behaving as expected. DuckMail renders and orchestrates; the backend does the mail work.

## Browser storage means clearing site data loses your temporary mailboxes

The usage notes are explicit about where state lives. Mailbox lists, login status and the API Key are kept in the current browser, and once you clear site data you have to log in again. The documentation goes further and warns you to save the address and password of any temporary mailbox yourself, because the web client will not be able to hand them back to you.

A second detail matters even more for anyone planning automation. Mailboxes created through the web interface do not expire by default, but how long the mail itself is kept is decided by the backend, and that retention window is independent of the account lifetime setting. In other words, the expiry field in the UI does not tell you when the messages disappear.

The consequence for an engineer is that DuckMail's browser storage is a convenience, not a system of record. If you need a durable inbox for a test account, keep the credentials and the address somewhere else. The project also exposes an owner API Key flow for hosted mailboxes, so a shared mailbox can be reached from another session without depending on one browser's local state.

## The /api/mail and /api/sse proxy routes return 403 to plain scripts

This is the sharpest constraint in the documentation and it is worth reading twice. Programmatic callers are told to use the backend API directly. The web client's own /api/mail and /api/sse endpoints verify that the request comes from a same-origin browser request, and a script that connects straight to them gets a 403 response.

The reason is visible in the environment variables. The proxy can carry a secret called DUCKMAIL_WEB_PROXY_SECRET, which the .env.example file describes as exempting web proxy requests from ordinary rate limiting and from the degraded behaviour applied to abnormal source addresses, while still not exempting account authentication. A comment in the same file points out that a public proxy can still be simulated by a script, which is why the value must never carry a NEXT_PUBLIC_ prefix.

A second variable, DUCKMAIL_TRUST_PROXY_HEADERS, defaults to false and should only be enabled when the web port is not public, an entry proxy overwrites X-Real-IP, and the backend is configured to trust that proxy. Otherwise the backend treats all web traffic as coming from the web server's own exit address and shares one limit across all users.

## A single docker run command is the shortest path to a local instance

The container deployment is the cleanest part of the project. The published image is syferie/duckmail-web:latest, it listens on port 3000, and the compose file sets NODE_ENV=production with a restart policy of unless-stopped.

```bash
docker run -d \
  --name duckmail-web \
  --restart unless-stopped \
  -p 3000:3000 \
  syferie/duckmail-web:latest
```

Once that container is up, you open http://localhost:3000 and the client is in front of you. The compose route in the repository does the same thing with the variables already wired for you.

```bash
docker compose up -d
```

The compose file passes DUCKMAIL_WEB_PROXY_SECRET and DUCKMAIL_TRUST_PROXY_HEADERS through from the host environment, with the latter defaulting to false. Its comment notes that an environment variable placed only in a host .env file is not passed into the container automatically, so a new variable has to be added to the environment section or loaded through env_file.

## Local development needs Node 20.9+ and pnpm, while the image builds on Node 22

For contributors, the development path is a clone, an install and a dev server. The engines field in package.json pins Node.js at 20.9.0 or newer, and pnpm is required rather than npm, which matters because the lockfile is pnpm-lock.yaml and the install uses the frozen lockfile flag.

```bash
git clone https://github.com/moonwesif/duckmail.git
cd duckmail
pnpm install --frozen-lockfile
pnpm dev
```

The dev server also answers on http://localhost:3000. Production builds use pnpm build, and the scripts block is small: dev, build, start and lint, all of them Next.js commands.

The Dockerfile is more opinionated than the local setup. It uses Node 22 and pnpm 10 as build arguments, runs the install with --ignore-scripts against a cached pnpm store, and produces a standalone build that it copies into the runtime stage as server.js. The runtime stage sets HOSTNAME=0.0.0.0 and PORT=3000, exposes 3000, and runs as the node user rather than root.

```dockerfile
EXPOSE 3000
CMD ["node", "server.js"]
```

The container also carries a healthcheck that polls the local port every 30 seconds with a 5 second timeout, 10 seconds of start period and 3 retries. That check is a plain HTTP GET to the root path, so it proves the process serves requests, not that the backend connection is healthy.

## Where DuckMail is the wrong tool

The first wrong-tool case is server-side automation that expects the web app's own endpoints. The same-origin check turns a 403 into a normal outcome for a script, and the documentation's answer is to call the backend API instead. If you want to poll mailboxes from a cron job or a CI pipeline, you are leaving the Next.js application behind and talking to a service that DuckMail does not ship.

The second case is a deployment where the upstream mail API is not reachable. The deployment section states that the target environment must support Next.js server-side execution and be able to reach the chosen mail API. Because the backend does the actual receiving and storage, a web tier that can reach the internet but not the mail API produces an interface that renders and then fails on every request that needs data.

A third case is anyone who needs an audit trail of deliveries. The client handles reading, attachment downloads and raw message viewing in the browser, but the backend controls retention, and the web interface creates temporary accounts that do not expire by default. Those are two different clocks, and only the backend one decides when the mail is gone.

On the licence side, the project is MIT, which is short enough to read in a minute and imposes the usual obligation to keep the copyright notice in copies. Nothing in the documentation adds any restriction on use of the temporary addresses themselves.

## Conclusion

DuckMail fits a team that wants a friendly web interface over temporary addresses or its own hosted Microsoft mailboxes, and that can run a Next.js server plus a separate mail backend. It does not fit a script-driven integration, because the /api/mail and /api/sse routes answer ordinary scripts with 403 and push callers to the backend API instead. Before adopting, confirm that your backend exposes the provider endpoints the client expects, set DUCKMAIL_WEB_PROXY_SECRET to at least 32 bytes if you use the proxy exemption, and read the healthcheck on port 3000 as the only health signal the container gives you.

## FAQ

### How do I run DuckMail locally with Docker?

Run the published image with the docker run command given above, or start the repository's compose file with docker compose up -d. Both expose the client on http://localhost:3000.

### Does DuckMail store my mailbox data on the server?

Mailbox lists, login status and the API Key are kept in the current browser, and mail reception and storage are handled by a separate backend. Clearing site data means logging in again, and the documentation advises saving temporary address and password details yourself.

### Why does my script get a 403 when it calls the DuckMail web API routes?

The /api/mail and /api/sse endpoints check for a same-origin browser request, so direct script calls are rejected with 403. The documentation tells programmatic callers to use the backend API directly instead.

### Can DuckMail manage hosted Microsoft mailboxes?

Yes. Hosted mailboxes that have been connected can be opened through a separate access password or an owner API Key, where you can read messages and check the sync status.

### What are the requirements for local development of DuckMail?

Node.js 20.9 or newer and pnpm, then pnpm install --frozen-lockfile followed by pnpm dev. The Docker build itself uses Node.js 22 and pnpm 10.

## Sources

- [Issues](https://github.com/MoonWeSif/DuckMail/issues)
- [License: MIT](https://github.com/MoonWeSif/DuckMail/blob/main/LICENSE)
- [MoonWeSif/DuckMail on GitHub](https://github.com/MoonWeSif/DuckMail)
- [Project website](https://www.duckmail.sbs/)
- [README](https://github.com/MoonWeSif/DuckMail/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/moonwesif-duckmail
