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

Application Admin interface, partner portal, REST API and unsubscribe endpoints. One container, or PHP behind Apache.
Worker Imports, publishing, scrub jobs and SFTP delivery, plus a few scheduled tasks.
MySQL Hashes, list versions, partners, keys, audit log.
S3 bucket Published list files and scrub results.
Amazon SES Sign-in links, invitations and alert email, from your own account.

Outbound

  • Partner systemsAPI downloads and scrubbing
  • Partner SFTP serversScheduled file delivery
  • Partner staffPortal downloads
There is no control plane on our side. The application does not contact us, and we cannot reach it.

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.

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

  2. Configure

    Settings live in a single environment file. The encryption key is generated on your machine and is never shared with us.

    .env
    # 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
  3. Start

    Bring up the application, then load the schema into your database.

    Shell
    docker compose up -d
    mysql -h db.internal.example.com -u optout -p optout \
      < database/schema_mysql.sql
  4. Schedule

    Run the worker as a service so it restarts on failure, and add the scheduled tasks to cron.

    optout-worker.service
    [Service]
    ExecStart=/usr/bin/php /var/www/app/bin/worker.php
    Restart=always
    User=www-data
  5. 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_KEY you 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_KEY somewhere 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.

Request pricing