Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Self-Hosted Revive Adserver

A production-ready Docker Compose stack for running Revive Adserver on a single VPS, with automatic Let's Encrypt SSL, persistent MariaDB storage, PHP tuned for ad delivery, and operator docs for two common tasks: wiring a VAST tag into a Bunny.net video player and configuring cookie-based frequency capping.

🇮🇱 מדריך התקנה בעברית עבור Kamatera + DuckDNS: docs/DEPLOY-KAMATERA.he.md

Revive Adserver ships no official Docker image. This repo builds Revive from a local source ZIP committed to docker/revive/revive-adserver-6.0.6.zip on top of php:8.2-apache — see docker/revive/Dockerfile. To upgrade Revive, drop the new ZIP in docker/revive/, bump REVIVE_VERSION in docker-compose.yml, and docker compose up -d --build revive.

Install is fully automatic on first container boot — no web installer to click through. See docker/revive/install-cli.php and docker/revive/entrypoint.sh. The bundled web installer (/www/admin/install.php) has a known redirect-loop bug in 6.0.6 that this approach side-steps.


Architecture

                       ┌────────────────────────────────┐
   client (browser /   │ nginx-proxy  +  acme-companion │  :80 / :443  (host)
   Bunny.net player) ─▶│  Let's Encrypt termination     │
                       └─────────────┬──────────────────┘
                                     │ HTTP, docker bridge
                                     ▼
                          ┌──────────────────────┐
                          │   revive (PHP/Apache)│
                          └─────────┬────────────┘
                                    │ TCP 3306
                                    ▼
                          ┌──────────────────────┐
                          │   db (MariaDB 10.11) │
                          └──────────────────────┘

revive will not start until db reports healthy (MariaDB's healthcheck.sh --connect --innodb_initialized). Only nginx-proxy publishes host ports — the database is never reachable from the public internet.


Prerequisites

  • A VPS with a public IPv4 and ports 80 and 443 open.
  • Docker Engine 24+ and Docker Compose v2 installed (docker compose version).
  • A domain you control, with an A record pointing to the VPS IP. Let's Encrypt's HTTP-01 challenge needs this to resolve before you bring the stack up.

Deploy to a VPS

# 1. Clone
git clone https://github.com/<your-user>/ad-server.git
cd ad-server

# 2. Configure secrets
cp .env.example .env
$EDITOR .env          # set REVIVE_DOMAIN, LETSENCRYPT_EMAIL, all *_PASS values

# 3. Verify the compose file parses with your .env
docker compose config >/dev/null

# 4. Bring it up (first run builds the Revive image — ~2–3 min)
docker compose up -d --build

# 5. Watch the install + cert (first boot, ~60–120s after DNS resolves)
docker compose logs -f revive
#   look for: "[install-cli] Done." then "Starting Apache..."
#   Ctrl-C once you see it, then:
docker compose logs -f acme
#   look for: "Creating/renewal of certificates for <your-domain>" → "success"

# 6. Open https://<REVIVE_DOMAIN>/  → login page (NOT an install wizard).
#    Use ADMIN_USER / ADMIN_PASSWORD from your .env.

Recommended: run Revive maintenance every 5 minutes

Revive's delivery stats, priority recalculation, and email reports run via a maintenance script. Add this to the host crontab (crontab -e):

*/5 * * * * docker compose -f /path/to/ad-server/docker-compose.yml exec -T revive php /var/www/html/scripts/maintenance/maintenance.php >/dev/null 2>&1

Getting the VAST Tag URL for a Bunny.net video player

The end goal is a single URL of the form https://<REVIVE_DOMAIN>/www/delivery/fc.php?zoneid=<N>&cb=... that you paste into Bunny.net Stream's VAST Tag URL field.

  1. Log in to Revive at https://<REVIVE_DOMAIN>/ with ADMIN_USER / ADMIN_PASSWORD.

  2. Create a Website — Inventory → Websites → Add new website. Give it a name like bunny-video and your site URL.

  3. Create a Video Zone under that website — Add new zone → Type: Video (NOT banner). Save and note the Zone ID shown in the URL/sidebar.

  4. Create an Advertiser — Inventory → Advertisers → Add new advertiser.

  5. Create a Campaign under that advertiser, then a Banner of type "Video (VAST)":

    • Paste a remote VAST XML URL (from your DSP / creative provider), or upload your own VAST 2.0/4.x XML.
  6. Link the banner to the video zone — open the banner → Linked Zones → check the video zone you created → Save.

  7. Grab the invocation code — open the video zone → Invocation Code tab → set Invocation Type to Video tag — VAST 2.0/4.x → copy the URL Revive displays. Example:

    https://ads.example.com/www/delivery/fc.php?zoneid=7&cb=INSERT_RANDOM_NUMBER_HERE
    
  8. Configure Bunny.net — Bunny.net Stream → your video library → Player tab → Advertising section → paste the URL into VAST Tag URL. Bunny.net automatically substitutes its cache-buster token in place of INSERT_RANDOM_NUMBER_HERE; if your version of the Bunny.net UI requires a literal token, use {random} or whichever token their docs specify.

  9. Verify — play the video. The pre-roll should fire. Within ~1 minute Revive's Statistics → Zones → <your zone> will show an impression.

Troubleshooting the VAST tag

Symptom Likely cause
Bunny.net player shows "no ad" Banner not linked to zone, or campaign weight/priority is 0.
Pre-roll plays but no impression in Revive Cache-buster not replaced — every request looks identical to Revive's anti-double-count logic. Make sure Bunny.net is substituting the token.
CORS error in browser console Revive must be reachable over HTTPS (it is, in this setup) and the VAST XML you serve must itself be HTTPS.

Configuring Frequency Capping (cookie-based)

Revive caps per-user impressions via the OAID cookie it drops on the viewer's browser. Caps are set on the campaign, not on the banner or zone.

  1. Inventory → Campaigns → <your campaign> → Delivery Options tab.
  2. Set Total impressions per user — e.g. 3.
  3. Set Time period — per day, per hour, or per session.
  4. (Optional) set Total clicks per user and Total conversions per user in the same panel.
  5. Save.

That's it. Revive now reads the OAID cookie on every delivery and skips this campaign for users who have hit the cap.

Cross-origin note (Bunny.net specifically)

Because the Bunny.net player loads from a different origin (e.g. iframe.mediadelivery.net) than your ad server (ads.example.com), the OAID cookie must be set with SameSite=None; Secure for it to be sent on the third-party VAST request. This stack guarantees both conditions:

  • TLS is always on in production (Let's Encrypt, no plaintext fallback).
  • config/php/revive.ini sets session.cookie_samesite = "None" and session.cookie_secure = 1.

If frequency capping silently doesn't work, open the browser devtools → Application → Cookies on the player page and verify OAID is present for ads.example.com with Secure and SameSite=None.


Maintenance & operations

Backups

# Dump the DB (writes to ./backup-YYYYMMDD.sql.gz on the host)
docker compose exec -T db \
  mariadb-dump -u root -p"$DB_ROOT_PASS" --single-transaction --routines --triggers "$DB_NAME" \
  | gzip > "backup-$(date +%Y%m%d).sql.gz"

# Banner creatives live in the revive_banners volume — back that up too:
docker run --rm -v revive-adserver_revive_banners:/data -v "$PWD":/backup alpine \
  tar czf /backup/banners-$(date +%Y%m%d).tar.gz -C /data .

Updating

Bump REVIVE_VERSION under services.revive.build.args in docker-compose.yml (e.g. 6.0.6 → 6.1.0), then:

git pull                              # pick up repo changes (Dockerfile, compose tweaks)
docker compose pull                   # refresh mariadb / nginx-proxy / acme
docker compose up -d --build revive   # rebuild the Revive image with the new version

Run Revive's database upgrade by visiting https://<REVIVE_DOMAIN>/www/admin/ after the rebuild — Revive auto-detects schema changes and walks you through them.

Logs

docker compose logs -f revive       # app + Apache access/error
docker compose logs -f db
docker compose logs -f acme         # cert issuance / renewal
docker compose logs -f nginx-proxy

Troubleshooting

Problem Fix
acme log shows urn:ietf:params:acme:error:dns or connection refused DNS A record hasn't propagated, or port 80 is firewalled. Verify dig +short $REVIVE_DOMAIN matches your VPS IP, and that nothing else binds :80.
Browser shows self-signed cert warning acme hasn't issued the real cert yet. Wait 1–2 min, then docker compose restart nginx-proxy.
Revive installer asks for DB host repeatedly Use literal hostname db (the service name), not localhost or 127.0.0.1.
revive container exits with DB connection error Check docker compose ps — db must show (healthy). If not, docker compose logs db for the root cause (most often a malformed password containing shell-special chars; quote it in .env).
Need to re-run the installer Stop the stack, remove the revive_var volume (docker volume rm revive-adserver_revive_var), bring it back up. This does not wipe the DB. Also drop the Revive schema if you want a truly clean install.
Cert renewal Automatic. acme renews 30 days before expiry. You can force it with docker compose exec acme /app/force_renew.

File layout

.
├── docker-compose.yml          # 4-service stack, healthcheck-gated startup
├── .env.example                # template — copy to .env, fill in, never commit
├── .gitignore                  # keeps .env and OS cruft out of git
├── config/
│   └── php/
│       └── revive.ini          # PHP + OPcache tuning, mounted into revive
└── README.md                   # this file

License

The configuration in this repository is released under the MIT License. Revive Adserver itself is licensed under the GNU GPL v2; consult upstream for the application's licensing terms.

About

Production-ready self-hosted Revive Adserver: Docker Compose, MariaDB, nginx-proxy + Let's Encrypt SSL, Bunny.net VAST integration.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages