Self-hosting
What you run, where it runs, and what it takes to keep it running.
OptOutManager is a web application, a background worker, a database and a storage bucket. All four live in your environment. This page is written for the person who will install it.
01 Architecture
Everything inside your boundary. Nothing outside it.
Inbound
- Your ESPsWebhook events over HTTPS
- RecipientsUnsubscribe links and one-click posts
- Your teamAdmin sign-in, imports
Your infrastructure
Outbound
- Partner systemsAPI downloads and scrubbing
- Partner SFTP serversScheduled file delivery
- Partner staffPortal downloads
02 Requirements
What to have ready before you start.
| Component | What is needed | Notes |
|---|---|---|
| Server | Linux with Docker, or PHP 8.2 and Apache | A virtual machine, a container service or a physical server. |
| Database | MySQL 8 | Run it yourself or use a managed service such as Amazon RDS. This is where suppression records live. |
| Object storage | Amazon S3 or an S3-compatible store | A custom endpoint and path-style addressing are supported, so MinIO and similar stores work on premises. |
| Email sending | Amazon SES | Used for system email only: sign-in links, invitations and alerts. It never sends to your subscribers. |
| Background jobs | One long-running worker and cron | The worker runs under systemd or a process supervisor. Scheduled tasks handle SFTP delivery, alert checks and cleanup. |
| Domain and TLS | A hostname and certificate | Unsubscribe links carry this hostname, so choose one you intend to keep. |
| Seed inboxes Optional | A subdomain with SES inbound | Needed only if you want seed addresses to detect a suppression file being mailed. |
| GeoIP database Optional | MaxMind GeoLite2, stored locally | Adds country and network context to the audit log without sending addresses to an outside service. |
03 Installation
Five steps from an empty server to the first list.
The commands here show the shape of the work for someone comfortable with Docker and a managed database. The guide that comes with the software covers each step in full.
-
Provision
Create the database, the bucket and an access policy that lets the application read and write that one bucket. Point a hostname at the server.
-
Configure
Settings live in a single environment file. The encryption key is generated on your machine and is never shared with us.
# generate with: php -r 'echo bin2hex(random_bytes(32));' APP_KEY= BASE_URL=https://optout.example.com DB_HOST=db.internal.example.com DB_NAME=optout DB_USER=optout AWS_REGION=eu-central-1 S3_BUCKET=example-optout-lists # raw addresses are not kept unless you change this STORE_RAW_EMAILS=false ADMIN_2FA=true -
Start
Bring up the application, then load the schema into your database.
docker compose up -d mysql -h db.internal.example.com -u optout -p optout \ < database/schema_mysql.sql -
Schedule
Run the worker as a service so it restarts on failure, and add the scheduled tasks to cron.
[Service] ExecStart=/usr/bin/php /var/www/app/bin/worker.php Restart=always User=www-data -
Sign in
Create the first administrator, enrol an authenticator app, then add a brand, a list and your first partner.
04 Security model
What is protected, and by what.
Self-hosting puts the boundary under your control. These are the measures inside it.
- Addresses
- Hashed on arrival as MD5, SHA-256 and SHA-512. Raw storage needs a global setting and a per-list setting, and both are off by default.
- Stored secrets
- SFTP passwords, private keys and webhook signing secrets are encrypted with libsodium, using a key derived from the
APP_KEYyou generate. - Administrator sign-in
- Authenticator-app codes with recovery codes, and the option to make two-factor mandatory. Sign-in attempts are rate limited.
- Partner access
- Bearer keys per partner, limited to the campaigns you grant, with optional IP allow-lists and per-minute limits.
- Downloads
- API downloads use signed links that expire. Every download is recorded against the partner who made it.
- SFTP delivery
- The partner’s host key fingerprint is pinned. If the key presented does not match, nothing is uploaded.
- Unsubscribe links
- Tokens are signed and carry an expiry, so a link cannot be altered to opt out somebody else.
- Network exposure
- Only unsubscribe links, inbound webhooks and partner access need to be reachable from the internet. The admin interface can be kept behind your VPN or an address allow-list at your proxy.
05 Operations
Looking after it once it is live.
The work is the same as for any small internal application, and it stays on your schedule.
- Backups
- Two things hold your data: the database and the bucket. Back up both, and keep a copy of
APP_KEYsomewhere safe. Without it, encrypted settings cannot be read. - Updates
- New versions come with numbered database migrations and a checklist. Nothing updates itself. You apply a release when it suits you.
- Health
- If the worker stops, imports and deliveries queue up until it is back. The dashboard shows failing SFTP destinations and webhooks, and a daily digest goes to your administrators.
- Retention
- How many list versions are kept and how long scrub results stay downloadable are settings. Old files are removed from the bucket by a scheduled task.
- Key rotation
- Partner keys are revoked and reissued from the admin interface. A documented procedure covers rotating the encryption key.
What we can see of your installation: nothing.
There is no shared service behind OptOutManager, no usage reporting and no remote access. If you need help, you decide what to show us.
The trade is that the installation is yours to run. If you would rather not operate another system, a hosted service will suit you better than this one.
One license. No meter running.
Tell us roughly how many records, brands and partners you manage, and where you plan to host. We will reply with a quote.