Deploy AdGuard Home
AdGuard Home: network-wide ad and tracker blocking DNS on Railway.
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/adguardhomeimage, releasev0.107.79 apache2-utils(htpasswd) for bcrypt password hashing at first bootsu-execfor privilege dropping tonobody- One Railway volume for config and query-log persistence
Deployment Dependencies
- Upstream project: AdguardTeam/AdGuardHome (GPL-3.0)
- Image:
wotonews/adguard-homeon Docker Hub (built from a private source repo) - Documentation: AdGuard Home Knowledge Base
Implementation Details
The custom entrypoint (entrypoint.sh) runs before the binary on every boot:
- Fixes ownership of
/opt/adguardhome(Railway volumes mount root-owned) and drops tonobody:nogroup. - On first boot, writes
AdGuardHome.yamlwith a bcrypt-hashed admin account fromAGH_ADMIN_USERNAME/AGH_ADMIN_PASSWORD— the setup wizard never runs, the UI is protected from minute one. - On every boot, rewrites
http.addressto0.0.0.0:${PORT}, so redeploying with a changed RailwayPORTnever 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 Name | Type | Source / Image | Persistent Volume | Public URL | Description |
|---|---|---|---|---|---|
adguard-home | Service | Docker image wotonews/adguard-home:v0.107.79-1 | Yes — /opt/adguardhome | Yes (web UI) | AdGuard Home DNS server with pre-seeded admin panel. |
⚙️ Environment Variables Config
🔑 Required Variables
| Variable Name | Default Value / Placeholder | Description | Security |
|---|---|---|---|
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 |
PORT | 3000 | Port the web UI listens on inside the container. Point the service domain at this port. | Standard |
🔩 Optional Configurations
| Variable Name | Default Value | Description |
|---|---|---|
AGH_ADMIN_USERNAME | admin | Admin 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(default3000). - 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
- Railway CLI Installed (
railwayon PATH) - Logged in to your Railway account (
railway login)
Local Setup
The template ships as a prebuilt image. To run it locally:
-
Configure:
cp .env.example .env # set AGH_ADMIN_PASSWORD -
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-hometag (orlatest), 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
Template Content
adguard-home
wotonews/adguard-home:v0.107.79-1AGH_ADMIN_PASSWORD
