---
title: "Deploy AdGuard Home"
description: "AdGuard Home: network-wide ad and tracker blocking DNS on Railway."
category: "Other"
url: https://railway.com/deploy/adguard-home
---

# Deploy AdGuard Home

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

**[Deploy AdGuard Home on Railway](https://railway.com/template/adguard-home)**

- **Creator:** wotonews
- **Category:** Other

## Template content

### adguard-home https://cdn.adguardcdn.com/website/adguard.com/products/screenshots/linux/agnar-linux.svg?nc=1

- **Image:** wotonews/adguard-home:v0.107.79-1

## Documentation

# 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`](https://hub.docker.com/r/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](#-updates-rebuilding-the-image).

---

## 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](#-dns-networking-on-railway) 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`](https://hub.docker.com/r/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

*   Upstream project: [AdguardTeam/AdGuardHome](https://github.com/AdguardTeam/AdGuardHome) (GPL-3.0)
*   Image: `wotonews/adguard-home` on Docker Hub (built from a private source repo)
*   Documentation: [AdGuard Home Knowledge Base](https://adguard-dns.io/kb/adguard-home/)

### 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

```mermaid
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` (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
*   [Railway CLI Installed](https://docs.railway.com/develop/cli) (`railway` on PATH)
*   Logged in to your Railway account (`railway login`)

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

1.  **Configure:**
    ```bash
    cp .env.example .env   # set AGH_ADMIN_PASSWORD
    ```

2.  **Run the published image (or build it yourself):**
    ```bash
    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
```bash
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:

```bash
# 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
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
```yaml
- name: Run E2E Integration Suite
  run: bash ./e2e/run-tests.sh
```

---

[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/new/template/adguard-home?utm_medium=integration&utm_source=button&utm_campaign=adguard-home)

## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/adguard-home
