Railway

Deploy AdGuard Home

AdGuard Home: network-wide ad and tracker blocking DNS on Railway.

Deploy AdGuard Home

Just deployed

/opt/adguardhome

Deploy and Host AdGuard Home with Railway

AdGuard Home is a free, open source, network-wide ad and tracker blocking DNS server. It acts as your private DNS sinkhole: queries for advertising and tracking domains are re-routed to a "black hole", while a built-in web dashboard manages filters, clients, and query logs.

This template deploys a pre-configured AdGuard Home container: an admin account is seeded automatically (no setup wizard), the web UI follows Railway's PORT, and all state persists on an attached volume.

> [!NOTE] > Source model — hybrid. The service runs the prebuilt image wotonews/adguard-home (pinned tag v0.107.79-1) for fast deploys. This repository is the source of truth for that image. Image-sourced services do not receive updatable-template PR branches — see Updates.


About Hosting AdGuard Home

Hosting AdGuard Home means running its single Go binary with a config file, a work directory for query logs and statistics, and the web admin UI on an HTTP port. In containerized form it is stateful: losing the config or work directory resets filters, settings, and history, so a persistent volume is mandatory.

On Railway, the web UI is served over a public domain and the admin credentials are generated at deploy time. The DNS resolver itself binds port 53 inside the container; see the DNS networking section for what is and is not reachable.

Common Use Cases

  • Personal or family-wide ad, tracker, and malware-domain blocking for every device, with no client-side software
  • Parental controls and SafeSearch enforcement via DNS filtering rules
  • Private, self-hosted alternative to Pi-hole with a maintained upstream
  • Per-client (device) filtering policies and live query-log inspection
  • DoH/DoT upstream resolution so your resolver's egress traffic is encrypted

Dependencies for AdGuard Home Hosting

  • Official adguard/adguardhome image, release v0.107.79
  • apache2-utils (htpasswd) for bcrypt password hashing at first boot
  • su-exec for privilege dropping to nobody
  • One Railway volume for config and query-log persistence

Deployment Dependencies

Implementation Details

The custom entrypoint (entrypoint.sh) runs before the binary on every boot:

  1. Fixes ownership of /opt/adguardhome (Railway volumes mount root-owned) and drops to nobody:nogroup.
  2. On first boot, writes AdGuardHome.yaml with a bcrypt-hashed admin account from AGH_ADMIN_USERNAME / AGH_ADMIN_PASSWORD — the setup wizard never runs, the UI is protected from minute one.
  3. On every boot, rewrites http.address to 0.0.0.0:${PORT}, so redeploying with a changed Railway PORT never breaks the web UI.

The binary is relocated to /usr/local/bin/AdGuardHome so a single volume can mount at /opt/adguardhome without shadowing it.

Why Deploy AdGuard Home on Railway?

Railway provides a single platform to host your DNS blocker alongside everything else — servers, databases, and other services in one project with private networking, vertical and horizontal scaling, and config-as-code. Deploy takes about a minute from the prebuilt image, credentials are generated for you, and state survives redeploys on the attached volume.


📐 Architecture & Components

graph TD
    User([Browser / Devices]) -->|HTTPS web UI| Domain[Railway Public Domain :PORT]
    Domain --> AGH[adguard-home Service]
    AGH --> Vol[(Volume at /opt/adguardhome)]
    AGH -->|DoH upstream| Internet[AdGuard DNS / Cloudflare]

📦 Services & Resources Included

Component NameTypeSource / ImagePersistent VolumePublic URLDescription
adguard-homeServiceDocker image wotonews/adguard-home:v0.107.79-1Yes — /opt/adguardhomeYes (web UI)AdGuard Home DNS server with pre-seeded admin panel.

⚙️ Environment Variables Config

🔑 Required Variables

Variable NameDefault Value / PlaceholderDescriptionSecurity
AGH_ADMIN_PASSWORD${{secret(20)}}Admin password for the web UI. Applied on first boot only; after that, password changes made inside the UI persist in the config volume. Auto-generated by the template.Sensitive
PORT3000Port the web UI listens on inside the container. Point the service domain at this port.Standard

🔩 Optional Configurations

Variable NameDefault ValueDescription
AGH_ADMIN_USERNAMEadminAdmin username seeded on first boot. Allowed characters: A-Z a-z 0-9 _ . @ -.

🌐 DNS Networking on Railway

Know what to expect before pointing devices at this deployment:

  • The web UI is fully served over the Railway public domain (HTTP). Point the domain's target port at PORT (default 3000).
  • Plain DNS (UDP/53) is not publicly reachable — Railway public networking proxies HTTP/TCP traffic only, and DNS clients require UDP. Treat this deployment as a web-managed filtering dashboard, or use the options below.
  • Private networking: other services in the same Railway project can query this resolver at adguard-home.railway.internal:53 (TCP).
  • TCP Proxy: a Railway TCP proxy domain can forward a public TCP port to in-container port 53, which covers DNS-over-TCP clients (dig +tcp, some stub resolvers). UDP-dependent clients will still not work through it.
  • Full protocol coverage (UDP DNS, DoT :853, DoH :443 with your own certs, DHCP) requires running the image elsewhere, e.g. on your LAN via docker run.

🚀 Setup & Deployment Guide

Prerequisites

Local Setup

The template ships as a prebuilt image. To run it locally:

  1. Configure:

    cp .env.example .env   # set AGH_ADMIN_PASSWORD
    
  2. Run the published image (or build it yourself):

    docker pull wotonews/adguard-home:latest
    docker run --rm -d --name agh \
      --env-file .env \
      -p 3000:3000 -p 53:53/udp -p 53:53/tcp \
      -v agh-data:/opt/adguardhome \
      wotonews/adguard-home:latest
    # UI: http://localhost:3000 — login with your .env credentials
    

Shipping to Railway

railway init                      # new project, or link an existing one
railway add --image wotonews/adguard-home:v0.107.79-1
railway variables --set "AGH_ADMIN_PASSWORD="
railway variables --set "PORT=3000"
railway volume add --mount-path /opt/adguardhome
railway domain                     # then set the domain target port to 3000

The healthcheck probes /, which redirects to the login page; a deployment turns healthy once the web UI answers.


🔄 Updates: Rebuilding the Image

This template is hybrid: the GitHub repo is the source of truth, and the Docker Hub image is rebuilt from it. As a template deployer, you can:

  • Stay pinned (default): the manifest references an exact tag (v0.107.79-1), so upstream changes never surprise you.
  • Track newer builds: point the service image at a newer wotonews/adguard-home tag (or latest), or deploy the repo directly and let Railway build the Dockerfile.

Maintainer workflow for a new upstream release:

# 1. Bump FROM adguard/adguardhome: in Dockerfile
# 2. Rebuild and publish
./scripts/push-to-dockerhub.sh adguard-home --tag -1
# 3. Bump the tag in railway.json and README, then redeploy

Image-sourced services do not get automatic updatable-template PR branches — updates flow through the tag stream above.


🧪 End-to-End (E2E) Testing

This template incorporates its own isolated E2E test suite under e2e/ to guarantee deployment integrity and configuration correctness.

Running Tests Locally

bash e2e/run-tests.sh

The suite validates the manifest, entrypoint contract, secret-exclusion rules, and — when Docker is available — builds the image, boots it, and asserts the web UI answers on the injected PORT with working admin credentials.

CI/CD Integration

- name: Run E2E Integration Suite
  run: bash ./e2e/run-tests.sh

Deploy on Railway


Template Content

More templates in this category

View Template
Rocky Linux
[Jul'26] Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀

codestorm
40
View Template
Foundry Virtual Tabletop
A Self-Hosted & Modern Roleplaying Platform

Lucas
71
View Template
Letta Code Remote
Run a Letta Code agent 24/7. No inbound ports, just deploy.

Letta
52