--- title: "How to Install Zammad Helpdesk on Ubuntu 24.04 with Docker" description: "Deploy the Zammad helpdesk on Ubuntu 24.04 with the official Docker Compose stack, publish it through Nginx with Let's Encrypt HTTPS and connect an email inbox." category: "Web Applications Hosting" language: en source: https://cubepath.com/docs/web-applications-hosting/zammad-helpdesk-installation updated: 2026-09-25 --- # How to Install Zammad Helpdesk on Ubuntu 24.04 with Docker Zammad is an open-source helpdesk and ticketing system: it turns emails, web forms, chat and phone calls into tickets that your team assigns, answers and tracks in one web interface. In this tutorial you will deploy Zammad on Ubuntu 24.04 using the official `zammad-docker-compose` stack, which already bundles PostgreSQL, Elasticsearch, Redis, Memcached and the Zammad services. You will then publish it through Nginx with a Let's Encrypt certificate, finish the setup wizard and connect a support mailbox. ## Prerequisites To follow this guide you need: - A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with at least 4 GB of RAM (8 GB is recommended once Elasticsearch has real data) and 2 CPU cores. - A non-root user with `sudo` privileges. - A domain or subdomain such as `helpdesk.your_domain` with a DNS A record pointing to `your_server_ip`. - Ports 22, 80 and 443 reachable from the Internet. - An email account (IMAP and SMTP credentials) that customers will write to, for example `support@your_domain`. Throughout the guide, replace `helpdesk.your_domain` with your own hostname. ## Step 1 - Installing Docker Engine and Docker Compose Ubuntu's `docker.io` and `docker-compose` packages lag behind upstream, and the old `docker-compose` v1 binary is no longer maintained. Install Docker Engine and the Compose plugin from Docker's official repository instead. Add Docker's signing key: ```bash sudo apt update sudo apt install ca-certificates curl git sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc ``` Add the repository: ```bash echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null ``` Install the engine and the Compose plugin: ```bash sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin ``` Check that the service is running and that Compose v2 is available: ```bash sudo systemctl is-active docker docker compose version ``` ```output active Docker Compose version v2.39.2 ``` Your version number will differ. All commands in this guide use `docker compose` (with a space), not the legacy `docker-compose`. ## Step 2 - Preparing the kernel for Elasticsearch Zammad uses Elasticsearch for its search, and Elasticsearch refuses to start in production mode if the kernel's `vm.max_map_count` limit is below 262144. Set it permanently with a sysctl drop-in file: ```bash echo "vm.max_map_count=262144" | sudo tee /etc/sysctl.d/99-zammad.conf sudo sysctl --system ``` Verify the new value: ```bash sysctl vm.max_map_count ``` ```output vm.max_map_count = 262144 ``` ## Step 3 - Downloading the Zammad Compose stack The Zammad team maintains a ready-made Compose project. Clone it into `/opt`: ```bash sudo git clone https://github.com/zammad/zammad-docker-compose.git /opt/zammad-docker-compose cd /opt/zammad-docker-compose ``` The repository contains `docker-compose.yml` and a `.env.dist` file that documents every supported variable with its default value. Do not edit those two files: they are overwritten when you update. Put your own values in a `.env` file instead, which Docker Compose reads automatically. Review the available variables first: ```bash cat .env.dist ``` Create your `.env` file: ```bash sudo nano /opt/zammad-docker-compose/.env ``` Add the following, replacing `your_strong_password` with a long random password: ```ini NGINX_EXPOSE_PORT=127.0.0.1:8080 POSTGRES_PASS=your_strong_password ``` `NGINX_EXPOSE_PORT` binds the stack's web entry point to localhost only. This matters because Docker publishes ports by writing its own iptables rules, which bypass UFW: a port published on `0.0.0.0` would be reachable from the Internet even with UFW enabled. The host's Nginx, which you configure in Step 5, will be the only public entry point. Setting `POSTGRES_PASS` before the first start replaces the default database password. > Note: If a variable name in `.env.dist` differs from the ones above in the version you cloned, use the name from `.env.dist`. ## Step 4 - Starting Zammad Pull the images and start the stack in the background: ```bash sudo docker compose up -d ``` The first start takes a few minutes: the `zammad-init` container creates the database schema and the search index, then exits. Check the status of the containers: ```bash sudo docker compose ps ``` All services except `zammad-init` should show `running` (or `Up`). `zammad-init` finishing with exit code 0 is expected. If something keeps restarting, read its logs: ```bash sudo docker compose logs -f zammad-init ``` Confirm that the web entry point only listens on localhost: ```bash sudo ss -tlnp | grep 8080 ``` ```output LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:* users:(("docker-proxy",pid=4121,fd=4)) ``` If you see `0.0.0.0:8080` instead, your `.env` value was not applied; fix it and run `sudo docker compose up -d` again. Finally, check that Zammad answers: ```bash curl -sI http://127.0.0.1:8080 | head -n 1 ``` ```output HTTP/1.1 200 OK ``` ## Step 5 - Configuring Nginx as a reverse proxy Install Nginx and Certbot on the host: ```bash sudo apt install nginx certbot python3-certbot-nginx ``` Create a server block for the helpdesk: ```bash sudo nano /etc/nginx/sites-available/zammad ``` Paste the following configuration. Zammad uses WebSockets for live updates in the agent interface, so the `Upgrade` and `Connection` headers must be forwarded: ```nginx map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 80; listen [::]:80; server_name helpdesk.your_domain; client_max_body_size 50M; location / { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 300s; } } ``` `client_max_body_size` allows attachments up to 50 MB. If another site on this server already defines a `map` named `$connection_upgrade`, remove the `map` block from this file to avoid a duplicate definition. Enable the site, test the syntax and reload Nginx: ```bash sudo ln -s /etc/nginx/sites-available/zammad /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` ```output nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` If UFW is active, allow SSH and web traffic: ```bash sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw status ``` ## Step 6 - Enabling HTTPS with Let's Encrypt Request a certificate. Certbot validates the domain over port 80, then adds the TLS configuration and an HTTP to HTTPS redirect to the server block: ```bash sudo certbot --nginx -d helpdesk.your_domain ``` Certbot installs a systemd timer that renews certificates automatically. Confirm that renewal works: ```bash sudo certbot renew --dry-run ``` ```output Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/helpdesk.your_domain/fullchain.pem (success) ``` ## Step 7 - Running the setup wizard Open `https://helpdesk.your_domain` in your browser. Zammad starts its setup wizard: 1. Choose **Set up a new system**. 2. Create the administrator account with your name, email and a strong password. 3. Enter your organization name, upload a logo if you want, and confirm the **System URL**. It must be `https://helpdesk.your_domain`, because Zammad uses it to build links in notification emails. 4. Configure email notifications. Select **SMTP** and enter the outgoing server of the mailbox Zammad will send from. You can skip this step and do it later. 5. Connect channels. You can add the email channel here or follow Step 8. After the wizard you land in the agent dashboard. Go to **Admin > System > Base** and check that **Fully qualified domain name** is `helpdesk.your_domain` and **HTTP type** is `https`. If these are wrong, links in emails will point to the wrong address. ## Step 8 - Connecting a support mailbox The email channel is what turns messages sent to `support@your_domain` into tickets. 1. Go to **Admin > Channels > Email** and click **Add Account**. 2. Enter the display name, the email address and its password. Zammad tries to detect the IMAP and SMTP servers automatically. 3. If detection fails, fill in the inbound (IMAP, usually port 993 with SSL) and outbound (SMTP, usually port 465 or 587) settings from your mail provider. 4. Choose the **Destination Group** that new tickets from this address will land in, for example **Users**. Zammad fetches new mail about once a minute. To verify the channel, send an email to `support@your_domain` from another account and wait for the ticket to appear in **Overviews**. Reply to it from Zammad and confirm that the reply arrives in the sender's inbox. > Important: Zammad deletes messages from the IMAP inbox after importing them unless you enable **Keep messages on server** in the account settings. Use a dedicated mailbox, not a personal one. With the channel working, create agent accounts under **Admin > Manage > Users**, assign them to groups, and define service levels under **Admin > Manage > SLAs** if you need response-time targets. ## Step 9 - Backups and updates The Compose stack includes a `zammad-backup` service that periodically dumps the PostgreSQL database and the Zammad files into a Docker volume. Find the volume: ```bash sudo docker volume ls | grep backup ``` ```output local zammad-docker-compose_zammad-backup ``` List its contents to confirm that backups are being written: ```bash sudo docker run --rm -v zammad-docker-compose_zammad-backup:/backup alpine ls -lh /backup ``` These backups still live on the same server. Copy them off the machine regularly, for example with `rsync` to another host or to object storage. To update Zammad, pull the latest version of the Compose project and recreate the containers: ```bash cd /opt/zammad-docker-compose sudo git pull sudo docker compose pull sudo docker compose up -d ``` Read the release notes before a major version upgrade, and make sure a recent backup exists first. ## Troubleshooting **The Elasticsearch container keeps restarting.** Check its logs with `sudo docker compose logs zammad-elasticsearch`. The usual causes are `max virtual memory areas vm.max_map_count [65530] is too low` (repeat Step 2) or the server running out of memory. Elasticsearch needs at least 1 to 2 GB of RAM for itself. **The browser shows 502 Bad Gateway.** Nginx cannot reach the stack. Run `sudo docker compose ps` in `/opt/zammad-docker-compose` and `curl -sI http://127.0.0.1:8080` on the server. During the first start Zammad needs a few minutes before it answers. **The agent interface keeps showing a connection warning.** WebSocket traffic is not being proxied. Make sure the `Upgrade` and `Connection` headers are in your Nginx configuration, then run `sudo nginx -t && sudo systemctl reload nginx`. **Emails are not converted into tickets.** Open **Admin > Channels > Email**: Zammad shows the last error for each account. Wrong credentials or an app password requirement on the mail provider are the most common causes. ## Conclusion You now have Zammad running on Ubuntu 24.04 from the official Compose stack, published only through Nginx with HTTPS, and receiving tickets from a support mailbox. As next steps, set up triggers and text modules under **Admin > Manage** to automate replies, add the web form or chat channel to your website, and schedule an off-server copy of the backup volume.