# Self-hosting simple-login/app: what the README actually walks you through

> The SimpleLogin back end and web app is a Flask service that turns your own domain into an email alias provider. The repository ships a self-hosting guide built around Docker, DNS records and a 2 GB Linux box, and the guide is the product here as much as the code.

**simple-login/app** — The SimpleLogin back-end and web app

- Repository: https://github.com/simple-login/app
- Website: https://simplelogin.io
- Stars: 7,026 · Forks: 652
- Language: Python
- License: AGPL-3.0
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/simple-login-app

## The problem simple-login/app solves, and who ends up running it

Reusing one address across every signup makes that address a tracking key. SimpleLogin's answer is the alias: a generated address that forwards to your real inbox, so the merchant, forum or newsletter never sees the address you actually read. The hosted service at simplelogin.io is the usual way to get that, but the repository exists so you can run the same thing yourself.

The README is explicit about the audience. It opens by saying it contains instructions on how to self host SimpleLogin, and it assumes you have a Linux server, a domain whose DNS you can edit, and enough patience to set up mail authentication records. Once the instance is running, the README states you can change the API URL in the Chrome or Firefox extension and the Android or iOS app to point at your server. That is the payoff: your aliases, your domain, your database, but also the vendor's official clients talking to your back end rather than theirs.

This is not a drop-in SaaS replacement for a non-technical user. It is for the person who already administers a mail domain or is willing to learn, and who wants alias generation to survive a provider shutting down or changing terms.

## How the alias pipeline is put together

The repository layout tells you the shape of the system before you read a line of Python. There is a Flask application under app/, a wsgi.py entry point, a server.py, an email_handler.py, a cron.py plus crontab.yml and crontab-all-hosts.yml, and a migrations/ directory driven by alembic.ini. That split is the architecture: a web process serving the app, a separate process that handles inbound mail, and scheduled jobs that run on a timer.

The Dockerfile confirms the web side. It builds static assets in a Node 10 Alpine stage, then runs the Python service on Ubuntu 22.04, installing dependencies with uv from uv.lock and exposing port 7777. The default command is gunicorn wsgi:app bound to 0.0.0.0:7777 with two workers and a 15 second timeout. Two workers with a 15 second timeout is a modest configuration, and it is worth reading as a signal about expected load rather than a tuning recommendation.

Mail delivery is the part that shapes everything else. The README's DNS section points an MX record for your alias domain at app.mydomain.com, the same hostname as the web app, so the box receives mail for the aliases and serves the interface. The README also mentions that the upload directory stores quarantine emails, which implies inbound mail is inspected before it is forwarded rather than passed straight through. The exact forwarding logic lives in email_handler.py, which the README does not document.

## Installing it: DNS first, Docker second

The README front-loads the DNS work, and that ordering is honest. Nothing works until mail for your domain resolves to your server, so the guide starts there. Install the DNS utilities used to check each record:

```bash
sudo apt update && sudo apt install -y dnsutils
```

Then create the directories the stack expects on the host. The README gives these three alongside a parent directory:

```bash
mkdir sl
mkdir sl/pgp # to store PGP key
mkdir sl/db # to store database
mkdir sl/upload # to store quarantine emails
```

Generate the DKIM key pair. The README chooses a 1024 bit key deliberately, noting that some registrars handle long TXT records poorly:

```bash
openssl genrsa -out dkim.key -traditional 1024
openssl rsa -in dkim.key -pubout -out dkim.pub.key
```

The public key has to be flattened into a single line before it goes into DNS. The README supplies a pipeline for exactly that:

```bash
sed "s/-----BEGIN PUBLIC KEY-----/v=DKIM1; k=rsa; p=/g" $(pwd)/dkim.pub.key | sed 's/-----END PUBLIC KEY-----//g' |tr -d '\n' | awk 1
```

With the keys in hand, the four records the README specifies are an MX record for mydomain.com pointing at app.mydomain.com with priority 10, an A record for app.mydomain.com pointing at your server IP, a TXT record at dkim._domainkey.mydomain.com holding the flattened public key, and a TXT record at mydomain.com with the value v=spf1 mx ~all. The README also recommends a DMARC record at _dmarc.mydomain.com with v=DMARC1; p=quarantine; adkim=r; aspf=r, and notes a stricter variant using p=reject with adkim=s and aspf=s for anyone who wants it.

Each record has a verification command in the README, all of the form dig @1.1.1.1 followed by the name and record type. Checking the MX record looks like this:

```bash
dig @1.1.1.1 mydomain.com mx
```

The README says this should return mydomain.com. 3600 IN MX 10 app.mydomain.com. If it does not, stop and fix the record before moving on, because the Docker steps assume mail routing already works. The README also warns that DNS changes can take up to 24 hours to propagate, though it adds that in practice it is much faster.

Docker comes next. If it is not installed, the README points at the Docker CE for Ubuntu instructions or the docker-install script, which it shows as a single line:

```bash
curl -fsSL https://get.docker.com | sh
```

After that the README moves into preparing a Docker network for the containers, which is where the excerpt ends. The remaining steps, including how the database, the web app and the mail handler are wired together, are in the parts of the README not shown here.

## The constraints that decide whether this is the right tool

Port 25 is the hard gate. The README lists it among the ports the server must have open, alongside 80, 443 and 22. Many residential ISPs and a number of cloud providers block outbound port 25 by default to limit spam, and if yours does, aliases will not forward and the setup will fail in a way that looks like a DNS problem. Confirm this before you spend an evening on records.

Memory is the second constraint. The README recommends at least 2 GB of RAM because most components run as Docker containers and Docker can be heavy. That is a floor, not a target, and it reflects a stack that includes PostgreSQL, the Flask app, the mail handler and scheduled jobs.

DKIM key length is a deliberate trade-off worth naming. The README says 1024 bits was chosen over 2048 for DNS simplicity, because some registrars do not handle long TXT records well. That is a reasonable call for a self-hoster fighting a registrar's input field, but it is a weaker key than a modern setup would use, and the README does not offer a path for anyone whose registrar handles 2048 bit records fine.

The clients are a limitation too. The README describes changing the API URL in the official extensions and mobile apps, which means the self-hosted instance is intended to be driven by SimpleLogin's own clients. The repository does not present a documented alternative client story, so you are tied to that surface for day-to-day use unless you build your own against the API. The pyproject.toml describes the package as the SimpleLogin partner API with OAuth2 and OpenID keywords, but the README does not walk through API usage, so anyone planning to integrate should expect to read the code.

## How this differs from running an alias service you do not host

The obvious alternative is using SimpleLogin's hosted service rather than deploying the repository. The difference is not features, it is who holds the keys. With the hosted service, someone else operates the mail servers, holds the database of alias mappings and handles deliverability. With the repository, you own the domain, the DKIM private key, the database directory and the quarantine store, and the README's setup makes that ownership concrete: sl/db and sl/upload are directories on your disk.

The cost moves with it. The hosted route costs money and trust. The self-hosted route costs a server, DNS administration and the ongoing job of keeping mail authentication correct, because a misconfigured SPF record or an expired DKIM key silently degrades forwarding into spam folders. The README's verification commands exist precisely because those failures are invisible from the web interface.

If you want aliases without administering mail infrastructure, this repository is the wrong choice, not a harder version of the right one. If you want the alias mapping to live on hardware you control, it is the point.

## Maintenance, licensing and what to check before committing

The project is not archived, and the last push was on 2026-09-18. Releases are frequent: v4.82.1 on 2026-09-17, v4.82.0 on 2026-09-15 and v4.81.7 on 2026-08-11. That cadence cuts both ways for a self-hoster. You get fixes, but you also inherit an upgrade treadmill, and the repository carries alembic.ini plus a migrations/ directory, which means schema changes arrive as migrations you have to apply. The README does not document a rollback procedure for a failed migration, so plan your database backups before upgrading rather than after.

Dependency pinning is another maintenance consideration. The Dockerfile pins uv to a specific version and verifies its download against a SHA256 hash, and the project ships both uv.lock and requirements.lock. That is good practice for reproducibility, and it also means the container build is sensitive to the state of those lockfiles. A Dockerfile that downloads a toolchain and verifies a hash will fail loudly if the artifact moves, which is preferable to failing quietly but still requires attention.

The licensing situation deserves care. The repository carries AGPL-3.0, while pyproject.toml declares license = "MIT" and describes the package as the SimpleLogin partner API. Those two statements do not agree. This is not legal advice, and the resolution depends on what you intend to do with the code, but anyone planning to run a modified version as a network service should read the AGPL-3.0 text in the repository and get their own answer rather than assuming either declaration wins.

## Conclusion

Adopt simple-login/app if you already run a Linux server with port 25 open and you are willing to own DKIM, SPF and DMARC records for a domain you control; the README treats that DNS work as a prerequisite, not an optional extra. Do not adopt it if you want aliases without touching DNS, or if your host blocks outbound port 25, because the documented MX record points your alias domain at the same host that runs the web app. Before you start, confirm three things: that your provider allows port 25, that you have at least 2 GB of RAM for the Docker stack, and that you can edit MX, A, TXT and DMARC records at your registrar. The repository's own pyproject.toml declares the package under an MIT licence while the repository carries AGPL-3.0, so settle that question before you build anything on top of the code.

## FAQ

### How much does a SimpleLogin subscription cost?

The repository does not state pricing for the hosted service. What it does cover is self-hosting, which the README frames around your own Linux server, domain and DNS rather than a subscription.

### Who is behind SimpleLogin?

The repository is published under the simple-login organization on GitHub, and pyproject.toml lists the author as SimpleLogin with the contact address dev@simplelogin.io. The homepage given for the project is simplelogin.io.

### Is SimpleLogin.com legit?

This article covers the open source repository, not the trustworthiness of the hosted service. What can be checked is the code itself: the back end and web app are published at github.com/simple-login/app under AGPL-3.0, and the README documents how to run your own instance instead of relying on anyone else's.

### How to use SimpleLogin correctly?

The README's route is to self host: install the DNS records it specifies, run the Docker stack on a Linux server, then point the API URL in the Chrome or Firefox extension or the Android or iOS app at your own server. Correctness in this setup depends on the DKIM, SPF and DMARC records being present and verifiable with the dig commands the README lists.

## Sources

- [License: AGPL-3.0](https://github.com/simple-login/app/blob/master/LICENSE)
- [Project website](https://simplelogin.io)
- [README](https://github.com/simple-login/app/blob/master/README.md)
- [Releases](https://github.com/simple-login/app/releases)
- [simple-login/app on GitHub](https://github.com/simple-login/app)

---

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