Why the Frontend Is Its Own Tier

The Zabbix frontend is the only part of the stack a non-engineer ever sees. It is also the part most likely to break in ways that look catastrophic but are operationally minor: when the frontend pod crashes mid-deploy, the on-call alert reads "Zabbix is down", the team panics, the actual server pair is still collecting and alerting normally, and forty minutes later you discover the only thing that failed was a containerised PHP renderer with zero state.

That is the whole reason the frontend deserves to live alone:

  • It is stateless. Every page render is a fresh database query. You can kill it, restart it, run two of them behind a load balancer, or deploy it red/black with no migration step. None of that is true for the server tier.
  • Its failure mode is loss of visibility, not loss of telemetry. Frontend down means humans cannot see graphs; collectors are still collecting and triggers are still firing. The pager response is "yawn, restart it", not "scramble".
  • Its dependencies are different. It needs the database (for HA-state lookup) and HTTP reach to the active server (for API calls). It does not need the proxy or the agents. That makes it easy to put on a different network, behind a different reverse proxy, or in a DMZ.

This post covers two deploy paths. The container version (Docker on the same monitoring VM) is the right choice for most homelabs and small environments. The bare-metal version (Apache + PHP) is what fits when your org disallows containers for whatever compliance reason, or when you want the frontend hosted on a load balancer that already runs Apache.

Zabbix Frontend setup

As I mentioned on my previous post, I'll be covering how to install the frontend using the official Zabbix Alpine Image docker image and on the server itself, in our case Ubuntu 24.04

This guide assumes that you are capable of deploying an ubuntu server on your own. If not, please follow this guide before proceeding.

Docker

If you don't know how to install docker and docker compose, please follow this Guide before moving on. Alternatively, you can skip to Server Install

1. Create a new directory

Before we deploy the container, create a new directory that will store the compose file. On my docker server, I have a dedicated directory /opt/Docker/ for all services I deploy using docker-compose. I'm going to create a folder named ZabbixWeb:

mkdir /opt/Docker/ZabbixWeb

2. Create and configure a docker-compose.yaml file

Create a new file named docker-compose.yaml in the previously created folder.

vim docker-compose.yaml

Modify the compose file to reflect your environment!

  • Container Name: zabbix_web
  • Ports: The zabbix frontend exposes the port 8080 on the container, and I'll be mapping that port to port 9015 on my host, since port 8080 is already in use. Feel free to set the port to 8080:8080 or 80:8080 to expose port 80 or 8080on your host.
  • Environment Variables: DB_SERVER_HOST: IP of the database server, in my case 10.0.0.20 POSTGRES_USER: The user we created, zabbixweb POSTGRES_PASSWORD: The password for the zabbixweb user PHP_TZ: The time zone, in my case America/Chicago
version: "3.3"
services:
  zabbix-web-nginx-pgsql:
    container_name: zabbix_web
    ports:
      - 9015:8080
    environment:
      - DB_SERVER_HOST=10.0.0.20
      - POSTGRES_USER=zabbixweb
      - POSTGRES_PASSWORD=zabbix
      - PHP_TZ=America/Chicago
    image: zabbix/zabbix-web-apache-pgsql:alpine-6.4-latest
networks: {}

3. Deploy the container

From the directory we've created, deploy the container with docker compose

docker compose up -d

Now you should be able to navigate to your hosts IP:Port and access the interface. In my case 10.0.0.250:9015

Done! The frontend should now be up and running!

Server Install

If docker is not for you, you can install the frontend on a standalone server.

Make sure your servers have a static IP address. Below is the /etc/netplan/*.yaml config I'm using

  • 10.0.0.23: Zabbix Web
  • 10.0.0.1: Gateway
  • 10.0.0.200: Internal DNS server
network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      addresses:
        - 10.0.0.23/24
      routes:
        - to: default
          via: 10.0.0.1
      nameservers:
        addresses: [10.0.0.200]

1. Install the zabbix repository

First, we need to download the repository with wget

wget https://repo.zabbix.com/zabbix/7.4/release/ubuntu/pool/main/z/zabbix-release/zabbix-release_latest_7.4+ubuntu24.04_all.deb

Once the download is done, install the packages using dpkg

sudo dpkg -i zabbix-release_latest_7.4+ubuntu24.04_all.deb

Now we need to update the packages

sudo apt update -y

2. Install the frontend

With the zabbix repository setup, we can install the frontend using apt

sudo apt install -y zabbix-frontend-php php8.3-pgsql zabbix-apache-conf

With the packages installed, we need to enable and start apache2

sudo systemctl enable apache2
sudo systemctl start apache2

It's always good practice to disable the default apache website

sudo a2dissite 000-default.conf
sudo systemctl reload apache2

3. Configure the Frontend

If you go to your server's IP address/zabbix http://10.0.0.23/zabbix you should see the setup screen

zabbix web setup screen

If you get an error on this step, check the errors I got during the installation process.

Once you get to the database config, your setup should look like the image below

db config fronted

Next set up the timezone and your preferred theme.

Remember that on this step we don't specify the zabbix server name because we've setup HA

settings frontend

Follow the remaining steps and you should get the congratulations message.

gongrats frontend

Done! The frontend should now be up and running!

Issues I've encouter

Missing Locale

During the server setup, I got the following error

locale error

The locale on the server is not setup properly, let's fix it.

Reconfigure Locale

If you don't have the locales tools installed, run

sudo apt clean && sudo apt update && sudo apt install -y locales

To start the configuration, run the command:

sudo dpkg-reconfigure locales

The output will look like the image below

localre reconfiguration

In my case, I need en_US, which is the option 97 from the image above

selectin locale

We need to select the default locale, which in my case is the option 3 for en_US

generating locale

Last thing to do is to restart apache2

sudo systemctl restart apache2

issue fixed

Done! The locale has been reconfigured, and you should be able to reload the page and continue with the configuration

What You Have Now

A working Zabbix frontend that can render the dashboard, walk the host inventory, and fail over with the server pair without you reconfiguring it. The HA-aware behaviour is the part that pays for itself: if the active server is currently zabbix02, the frontend reads that from the ha_node table on every API call, so you never have to manually point the frontend at "the new active server" after a failover.

Verify It Before You Move On

Three checks are worth doing before you build the proxy on top:

  1. Confirm both servers appear in the frontend. Reports → System information → High availability cluster. You should see both HANodeNames, one as Active, one as Standby. If only one shows, the standby never registered, the database connection from that server is broken.
  2. Test failover from the frontend's point of view. Stop zabbix-server on the active node (as the server post describes). Refresh the dashboard. After 60 seconds, the system-information page should show the standby promoted to Active and the dashboard should keep rendering normally.
  3. Lock the locale in image config, not by manual dpkg-reconfigure. If you hit the locale error documented above, fix it once in your base image (or Dockerfile, or cloud-init), not interactively. Manual fixes don't survive the next rebuild.

Next in the Series

The proxy post builds the collection layer that buffers data from agents and ships it to the active server. After that, the agent post installs the collectors on the hosts you actually want to monitor. With those two in place, the build from the overview post is complete.