# Nortix Mail: TLS optional, and a config file named three different ways

> Zhoros/NortixMail is a self-hosted disposable email server in Node with a Svelte front end, chosen for making TLS optional and auto-detecting certificates by inspecting the files rather than the names, but its build instructions name a front directory the repository does not have, its configuration is called config.json in the prose and config.js in the tree, and the only npm script is a dev watcher.

**Zhoros/NortixMail** — Nortix Mail - disposable email server with an easy setup

- Repository: https://github.com/Zhoros/NortixMail
- Stars: 717 · Forks: 56
- Language: Svelte
- License: NOASSERTION
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/zhoros-nortixmail

## The install instructions name a front directory that is not there

The Docker route is two steps: get the repository, then run `docker compose up -d`. The route without Docker is where the documentation drifts. It says to make sure Node.js and npm are installed, then runs a numbered list that goes one, two, four, five, six, seven, eight, with the missing third step, and the body of it is:

```bash
npm install && cd front && npm install && npm run build && cd .. && node main.js
```

`front` is the directory that does not exist. The top-level listing has `public/`, `svelte/`, and `data/`, and both the Dockerfile and the `dev` script refer to `svelte`. So the documented manual install path cannot complete as written, and the one-word fix is to substitute the name the code actually uses. The server itself starts with `node main.js`, the HTTP listener is on port 80, and the documentation reminds you that port 25 has to be reachable or no mail will arrive.

## The configuration is called config.json in the prose and config.js in the tree

The configuration section reads as though the file name were uncertain even to the author. It says you can edit `config.json` inside `data/config.json`, which names two different paths in one sentence, and the repository root contains `config.js` rather than either. Inside the Docker image the situation resolves itself only by accident: the Dockerfile copies the whole repository into `/app`, and the compose file then mounts `./data` over `/app/data`, so the file that matters at runtime is whatever sits in your local `data` directory. What the documentation does say about the contents is narrow, naming two settings, the mail refresh interval and the number of emails shown per page. There is no environment variable for either in the compose file, which maps no variables at all, so the YAML file in the mounted directory is the whole configuration surface in a container deployment.

## There is no build or start script, only a dev watcher

The manifest has exactly one script, and it is for development:

```json
"dev": "concurrently --kill-others \"nodemon main.js\" \"npm --prefix ./svelte run start\""
```

There is no `build` and no `start` at the root, which is why the documentation tells you to run `node main.js` yourself rather than to use npm. The script also confirms that the front end directory is called `svelte`, running the front end's own start script through a prefix flag while nodemon watches the server. Two other details in the same file are worth noting. The package is named `mail` at version `1.0.0`, and both the `author` and `description` fields are empty strings, which means anything published from this manifest would carry no identifying metadata.

## Port 25 has to stay mapped, and reverse proxies are the reason

The compose file is ten lines and every one of them matters:

```yaml
services:
  mail:
    build: .
    ports:
      - "25:25"
      - "80:80"
    volumes:
      - ./data:/app/data
```

Port 25 is the SMTP receiver and port 80 is the web interface, so both are published. The documentation goes out of its way on the 25 mapping, saying it is recommended not to change it if you are behind a reverse proxy because some reverse proxies cannot forward SMTP packets. That is a real operational constraint rather than a stylistic preference: a reverse proxy that terminates HTTP cleanly on 80 will silently break mail delivery on 25 unless it passes that port through untouched. Publishing 25 from a container also means the host has to be able to bind it, which is a privilege question as much as a networking one.

## TLS is optional and the server guesses which file is which

This is the part the project is genuinely good at, and it is why it exists. Certificates are optional, so the common case needs no certificate work at all. When you do want them, you copy the certificate and the private key into the `data` folder, usually with `.crt` and `.key` extensions, and then this sentence: the file name and extension do not matter because the server can automatically detect which one is which. That removes the single most tedious part of running a mail server, which is matching filenames to expectations. The honesty about the consequence is equally useful. The explanation given is that the mail transfer protocol is very old and does not require TLS by default, that anyone in the path can read a message if they choose to intercept it, and that the parties with that capability are mostly internet service providers and hosting providers, which is why interception is unlikely rather than impossible.

## Tailwind is a runtime dependency, and the list is otherwise small

Six production dependencies is a short list for something that stores mail, parses it, receives it over SMTP, and serves a web interface. `better-sqlite3` is the store, `express` is the HTTP server, `smtp-server` receives the messages, `mailparser` reads them, `compression` shrinks the responses, and `tailwindcss` at `^4.3.0` styles the front end. That last one is the odd placement, since a stylesheet tool is a build-time concern and the compiled output is what ships. The two development dependencies are `concurrently` and `nodemon`, which exist only to serve the `dev` script. There is no test framework in either list and no test script at all, and the top-level listing has no continuous-integration directory, so nothing in the repository is set up to verify that a change did not break the SMTP path.

## The image installs Node on top of the Node image

The Dockerfile opens with a comment explaining the base choice, which is to use an older version to prevent issues, and picks `node:20-alpine`. Then it runs `apk add --no-cache git nodejs npm`, which installs the distribution's own Node and npm into an image that already contains a specific Node runtime from the official image. That is at best redundant and at worst replaces the pinned runtime with whatever Alpine ships. The rest of the build is straightforward: copy everything in, install production dependencies at the root, change into the front end directory, run an unqualified `npm install` there so development dependencies land in the image, build it, change back, and start `node main.js`. Nothing in the image pins a version for the front end's own dependencies, so that half of the build is as reproducible as the lockfile in the directory you cloned.

## No pull requests, and a licence that names three different things

The contributing section is two sentences and both matter. It states that the repository currently does not accept any pull request, while inviting issues to request a feature, report a bug, or ask a question. So the project is open in the sense of being readable and forkable and closed in the sense of having no review process described anywhere. Licensing is a three-way mismatch worth resolving before you deploy it. The repository metadata reports no recognised licence identifier, the manifest declares `ISC` in its licence field, and a `LICENSE` file sits in the root. Any of those could be the right answer, but only one of them is authoritative and nothing in the repository says which. For a service that receives mail, resolve that before you point a domain at it.

## Conclusion

Nortix Mail solves one unglamorous problem properly: running your own SMTP receiver is miserable because of certificates and DNS, and this makes the certificate step optional and then guesses which file is the certificate and which is the key rather than asking you to name them. Moving it is a folder copy, which is the right answer. The documentation is where it gets thin. The build path tells you to change into a directory called `front` that does not exist in the repository, the configuration is named `config.json` in the prose and `config.js` in the tree, and the compose file passes no environment variables at all, so that one file is the entire configuration surface in Docker. Before you rely on it, run the two install routes and see which one actually completes, then decide whether you need TLS, because the documentation itself is candid that mail without it is readable in transit.

## FAQ

### What is Nortix Mail?

A self-hosted disposable email server. It listens for SMTP on port 25 and serves a web interface on port 80, so you can create throwaway addresses for signing up to sites that require email verification without handing over your real address.

### How do I run Nortix Mail with Docker?

Clone or download the repository and run `docker compose up -d`. The compose file builds the image locally, maps ports 25 and 80, and mounts `./data` at `/app/data`, which is where the configuration, the SQLite database, and any certificates live. The documentation advises leaving the 25 mapping alone when a reverse proxy is in front.

### Do I need TLS for Nortix Mail?

No, it is optional. When you do want it, copy your certificate and private key into the `data` folder, and the file names and extensions do not matter because the server works out which file is which. Mail sent without TLS can be read in transit, with interception mostly limited to internet providers.

### How do I move Nortix Mail to another server?

Copy the `data` folder. That directory holds the configuration file, the SQLite database, and any certificates, and the documentation describes this as the entire procedure for both changing your domain and changing machines. TLS certificates placed there are also auto-detected after the move.

## Sources

- [Issues](https://github.com/Zhoros/NortixMail/issues)
- [README](https://github.com/Zhoros/NortixMail/blob/main/README.md)
- [Zhoros/NortixMail on GitHub](https://github.com/Zhoros/NortixMail)

---

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