Skip to content

zorgch/zorg-docker

Repository files navigation

zorg on Docker

Portable, Server independent, Docker-based code to get the zorg Websites and Services up, running, and hosted.


Table of Contents

πŸ”– Pre-requisites

🏁 Getting started

πŸ“¦ Docker services

πŸ‘¨β€πŸ« Explanations




πŸ”– Pre-requisites

git installation

Install git for your OS.


Docker installation

Following the official installation instructions for Docker.

Tip

On Ubuntu it's advised against installing via snap, as this may cause compatibility issues!


🌐 DNS-records and Hosts

For all Hosts (subdomains) on the main Domain, the correspoinding DNS A-records with IP must be set up.

Example A-records
mail.domain.ch.	        600	IN	MX	178.nn.nn.nn
*.domain.ch.	          600	IN	A	  178.nn.nn.nn
www.domain.ch.	        600	IN	A	  178.nn.nn.nn
dockerstatus.domain.ch.	600	IN	A	  178.nn.nn.nn

πŸ‘¨β€πŸ’» Working locally (development)? Add hosts!

On production a proper setup with pointing DNS for the root domain to the server's IP-address, this should not be necessary. But locally with a dummy domain, the domain & hostnames must be added to the /etc/hosts-file:

Example `hosts`-entries

(adjust as per your .env settings)

127.0.0.1	zdocker.dev
127.0.0.1	status.zdocker.dev
127.0.0.1	www.zdocker.dev
127.0.0.1	db.zdocker.dev
127.0.0.1	ftp.zdocker.dev
127.0.0.1	irc.zdocker.dev
127.0.0.1	pw.zdocker.dev
127.0.0.1	smtp.zdocker.dev
127.0.0.1	quake.zdocker.dev



🏁 Getting started

In general make sure to work from the project root directory:

cd /srv/<my-website>/<host>


πŸ“‚ Folder structure setup

Creat the a folder structure on your host machine that reflects the following:

Important

This is just a proposal, folder structures & names can be different!

└── www               <-- (Your project root directory)
    β”‚
    β”œβ”€β”€ zorg-docker/       <-- Pulled Git repository (repo)
    β”‚
    β”œβ”€β”€ .env               <-- Copy & adjust ".env.example" from repo
    β”œβ”€β”€ docker-compose.yml <-- Symbolic-linked ./zorg-docker/docker-compose.yml
    β”œβ”€β”€ docker-update.sh   <-- Symbolic-linked ./zorg-docker/docker-update.sh
    β”‚
    β”œβ”€β”€ reverseproxy/      <-- (Optional) To further customize Traefik middlewares (IP-Whitelist etc.). Ref in .env
    β”‚Β Β  └── middlewares-http.yaml
    β”‚
    β”œβ”€β”€ reverseproxy-waf/  <-- Copy & adjust "waf/rules/"-dir from repo. Ref in .env
    β”‚Β Β  └── zorg-appsec.yaml   <-- (Optional) To further customize WAF rules.
    |
    β”œβ”€β”€ website/           <-- zorg Website configs & data
    β”‚   β”œβ”€β”€ .env           <-- .env file for Website
    β”‚   β”œβ”€β”€ apache.conf      <-- Copy & adjust "website/apache/example.conf" from repo
    β”‚   β”‚   └── data/          <-- Website /data/ folder & files
    β”‚   β”‚Β Β  β”œβ”€β”€ files/         (user generated content for zorg website)
    β”‚   β”‚Β Β  β”œβ”€β”€ gallery/
    β”‚   β”‚Β Β  β”œβ”€β”€ tauschboerse/
    β”‚   β”‚   └── ...
    β”‚   β”œβ”€β”€ cronjobs/
    β”‚   β”‚Β Β  └── cronjobs.crontab   <-- Copy & adjust "website/php/example.crontab" from repo
    β”‚   └── sendmail/
    β”‚   Β  Β  └── msmtprc    <-- Copy & adjust "website/sendmail/example-msmtprc" from repo
    β”‚
    β”œβ”€β”€ mailserver/        <-- (Optional) To further customize Postfix SMTP. Reference in .env
    β”‚Β Β  └── postfix-main.cf
    |
    β”œβ”€β”€ irc/                  <-- IRC server config files (path set in .env as HOST_PATH_IRC_IRCD_CONFIG_FILES)
    β”‚   β”œβ”€β”€ ircd.yaml         <-- Copy & adjust "resources/irc/ircd.yaml" from repo (⚠️ add opers block!)
    β”‚   └── ircd.motd         <-- Copy & adjust "resources/irc/ircd.motd" from repo
    β”‚
    β”œβ”€β”€ code-docu/
    β”‚Β Β  β”œβ”€β”€ code/       <-- (Optional) Git clone of github.com/zorgch/zorg-code.git. Reference in .env
    β”‚Β Β  β”œβ”€β”€ docu/       <-- (Optional) Reference in .env
    β”‚Β Β  └── phpdoc.xml     <-- (Optional)
    β”‚
    β”œβ”€β”€ keepass/         <-- Reference in .env Only AFTER sftp started: put kdbx file here.
    β”‚
    β”œβ”€β”€ quake3-baseq3/   <-- Reference in .env
    β”‚Β   β”œβ”€β”€ q3config_server.cfg   <-- Copy & adjust "quake3/example-server.cfg" from repo
    β”‚Β Β  β”œβ”€β”€ pak0.pk3              <-- From a local licensed Quake3 installation
    β”‚Β Β  └── pak1-8.pk3            <-- Can be obtained at: https://ioquake3.org/extras/patch-data/
    β”‚
    └── logs/              <-- Reference in .env
        β”œβ”€β”€ website/       <-- Sub-directories MUST also be created manually!
        β”‚   β”œβ”€β”€ apache/
        β”‚   β”œβ”€β”€ php/
        β”‚   β”œβ”€β”€ sendmail/
        β”‚   └── website/
        β”œβ”€β”€ reverseproxy/
        β”œβ”€β”€ mariadb/
        β”œβ”€β”€ mailserver-smtp/
        β”œβ”€β”€ irc-server/
        β”œβ”€β”€ sftp/
        └── quake3-server/

πŸ’Ύ Docker images

Here's an overview of the underlying Docker images used for the Docker Services, in order to provide quick access to their documentation & configuration how-to's.

Service Docker image Link
πŸ” sslcerts alpine/mkcert GitHub
πŸ“Š dashboard portainer/portainer-ce Docs
🚨 servicealerts lorcas/docker-telegram-notifier GitHub
🚦 reverseproxy traefik
crowdsec-bouncer-traefik-plugin
Docs
Plugin
πŸ›‘οΈ waf crowdsecurity/crowdsec Docs
☣️ waf-dashboard ghcr.io/theduffman85/crowdsec-web-ui GitHub
🌐 website php Docker Hub
πŸ’½ db mariadb Docs
πŸ‘¨β€πŸ’» db-manager adminer Docs
πŸ“§ postfix-smtp mailserver/docker-mailserver Docs
πŸ’¬ irc ghcr.io/ergochat/ergo GitHub
🧩 irc-quizbot python:3-slim GitHub
πŸ•ŠοΈ irc-telegram-bridge bhavin192/teleirc Docker Hub
πŸ“ˆ stockticker python:3-slim GitHub
πŸ”‘ sftp atmoz/sftp Docker Hub
πŸ•ΉοΈ quake3 jberrenberg/quake3 GitHub
πŸ“‘ phpdoc phpdoc/phpdoc Docs

🧬 Docker Networks

In order to not block Ports for other networking services on the server / in other Docker stacks, this Docker stack has support for HTTP, TCP (dedicated), and UDP shared networks (aka External Docker Networks).

These are optional, but highly recommended to use - in order to prevent future port conflicts. Here's a schematic overview of the networking capabilities added:

graph TD
  %% Externe Netzwerke
  subgraph External Networks
    lb_http["loadbalance-http<br>[external/shared]"]
    lb_tcp["loadbalance-tcp<br>[external/shared]"]
    lb_udp["loadbalance-udp<br>[external/shared]"]
  end

  %% zorg Main
  subgraph zorg Live
    zorg[zorg services]
    grid["the-grid<br>β†’ loadbalance-http"]
    superhighway["information-superhighway<br>β†’ loadbalance-tcp"]
    slipgate["slipgate-teleporter<br>β†’ loadbalance-udp"]
    zion["zion-mainframe<br>[internal only]"]
  end

  %% zorg Construct
  subgraph zorg Construct
    stack1[construct services]
    stack1_http["β†’ loadbalance-http"]
    stack1_tcp["β†’ loadbalance-tcp"]
    stack1_udp["β†’ loadbalance-udp"]
    internalnet["custom-net<br>[internal only]"]
  end

  %% Weitere Stacks
  subgraph other-stack-2
    stack2[stack 2 services]
    stack2_http["β†’ loadbalance-http"]
    stack2_internalnet["stack2_default<br>[internal only]"]
  end

  subgraph other-stack-3
    stack3[stack 3 services]
    stack3_tcp["β†’ loadbalance-tcp"]
    stack3_udp["β†’ loadbalance-udp"]
  end

  %% Verbindungen zorg
  zorg --> grid --> lb_http
  zorg --> superhighway --> lb_tcp
  zorg --> slipgate --> lb_udp
  zorg --> zion

  %% Verbindungen andere Stacks
  stack1 --> stack1_http --> lb_http
  stack1 --> stack1_tcp --> lb_tcp
  stack1 --> stack1_udp --> lb_udp
  stack1 --> internalnet

  stack2 --> stack2_http --> lb_http
  stack2 --> stack2_internalnet

  stack3 --> stack3_tcp --> lb_tcp
  stack3 --> stack3_udp --> lb_udp
Loading



Initial setup (one time only)

Git clone the zorg-docker repository

git clone -b <branch-name> --depth 1 https://github.com/zorgch/zorg-docker.git ./zorg-docker

Note

See below section for how to UPDATE the cloned git repository to get its latest changes.

Edit a copy of the .env-file
cp ./zorg-docker/.env.example ./.env

Important

Using your text editor of choice, adjust the .env-file to the setup of your host machine.

Create a symbolic link to docker-compose.yml
ln -s ./zorg-docker/docker-compose.yml ./docker-compose.yml

Add external Docker networks

These networks allow OTHER Docker Stacks and Services to connect to the same network.

docker network create loadbalance-http
docker network create loadbalance-tcp
docker network create loadbalance-udp

Note

Why is this important? A: Access to Docker Services in the Stack from other Docker Stacks and Services. B: This is particularly important to use 1 central Reverse-Proxy to route traffic to the services in the correct Stack. C: Conclusion of A & B means: no Port blockings of common Ports (e.g. 80 or 443) by 1 single Docker Stack!

Validate the Docker services configurations

docker compose build

⏩ Update the local cloned zorg-docker git repository

cd into the directory containing the locally cloned git files, and run a git pull:

cd /srv/<my-website>/<host>/zorg-docker
git pull --rebase

Important

Check the updated Files and apply necessary changes to outdated manual copies of the same!

  • Example:
Fast-forward
 .env.example                                 |  17 +++++--------
 docker-compose.yml                           |  75 ++++++++++++++++++++++++++++++++++++-------------------
 resources/irc/unrealircd/unrealircd.conf     |  12 ++++-----
 resources/mailserver/postfix-main.cf         |   4 +--
 resources/reverseproxy/middlewares-http.yaml | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
 resources/reverseproxy/selfsigned-certs.yaml |  23 ++++++++---------
 6 files changed, 207 insertions(+), 90 deletions(-)

πŸ” TLS/SSL: generate self-signed certificates

Some services require self-signed certificates, this does not interfere with (also) using Let's Encrypt certificates!

Add these first using the sslcerts service:

docker compose --profile setup up

☣️ WAF Dashboard: register watcher service (one time only)

Generate a password, set it in .env as CROWDSEC_WEBUI_KEY, then register it on CrowdSec:

  1. Generate & save password into .env
openssl rand -hex 32
# β†’ set as CROWDSEC_WEBUI_KEY in .env
  1. Register the service as watcher
docker exec PROJECTNAME-crowdsec cscli machines add crowdsec-web-ui --password "$(grep CROWDSEC_WEBUI_KEY .env | cut -d= -f2)" -f /dev/null

πŸ“§ Mailserver (SMTP) configuration

Note

This requires the mailserver postfix-smtp service to be running!

Add postfix accounts & email forwarding

Tip

This is required when emails to a local user (alias) should be forwarded to an external email address corresponding to that alias.

docker exec -ti PROJECTNAME-mailserver setup email add info@DOMAINNAME <NEW_PASSWORD>
docker exec -ti PROJECTNAME-mailserver setup alias add <EMAILADDRESS> <RECIPIENT>
General mailserver setup help

The docker-mailserver setup is required for various configurations, including for example DKIM.

docker exec -ti PROJECTNAME-mailserver setup
docker exec -ti PROJECTNAME-mailserver setup config dkim help

πŸ’¬ IRC server setup

Note

This requires the irc service to be running!

Copy the template config from the repo to your HOST_PATH_IRC_IRCD_CONFIG_FILES directory and add at least one opers: block with a bcrypt password hash:

# Generate a bcrypt hash for each oper password:
docker exec -ti PROJECTNAME-ircdserver /ircd-bin/ergo genpasswd

Then add opers: to the ircd.yaml config-file:

opers:
    nickname:
        class: "server-admin"
        password: "<hash-from-genpasswd>"
        modes: +is
Nickname registration & email-verification (by email verification)

Connect to the IRC server and register your nickname, to oper with it:

Note

Once you've registered, you'll need to set up SASL to login!.

/msg NickServ REGISTER <password> <email>
# Check email with verification code
/MSG NickServ VERIFY <nickname> <verification-code>
/OPER yourname <password>
Channel registration

Connect to the IRC server authenticated with your nickname, then oper and register a channel:

/OPER yourname <password>
/msg ChanServ REGISTER <#channel>

Regain Ops on a registered channel:

/msg ChanServ OP <#channel>

Give someone else OP mode or so, for a channel:

/msg ChanServ AMODE <#channel> <modes> <accountname>

# For example, AMODE #channel +o dan grants the holder of the "dan"
# account the +o operator mode every time they join #channel. To list current
# accounts and modes, use: /msg ChanServ AMODE #channel
# Note that users are always referenced by their registered account names
# not their nicknames.

Tip

See Modes documentation

Bot/service accounts registration (bypasses email verification)

Connect to the IRC server, and oper yourself:

/OPER <nickname> <password>
/msg NickServ SAREGISTER quizhans <password>
/msg NickServ SAREGISTER telegram <password>



πŸ“¦ Docker services

Manage general services

PRODUCTION mode – run in "detached mode" (background), without interative logging to the shell by adding the -d flag.

Start/stop all services *
docker compose --profile all up -d
  • Applicable services: servicealerts, dashboard, reverseproxy, website, db, postfix-smtp, irc, irc-quizbot, irc-telegram-bridge, stockticker, sftp, quake3
Example: only the Webserver services
docker compose --profile webserver up -d
  • Applicable services: servicealerts, dashboard, reverseproxy, website, db, postfix-smtp
Example 2: only the IRC services
docker compose --profile irc up -d
  • Applicable services: servicealerts, dashboard, irc, irc-quizbot, irc-telegram-bridge
Example 2: only the Mailserver services
docker compose --profile mailserver up -d
  • Applicable services: servicealerts, dashboard, reverseproxy, postfix-smtp

Caution

Do not take an individual service down using --profile, target it specifically instead!
docker compose down stockticker


Run the KeePass SFTP-service (separately)

* As provisioning a KeePass KDBX via SFTP is not required for the general website hosting, the SFTP service (keepass) is separated from the overall services.

docker compose --profile keepass up -d
docker compose down sftp

Run the Quake 3 Arena Server (separately)

* Due to a potential high load on the server, the Β«Quake 3 ArenaΒ» Server (quake3) is separated from the general services.

docker compose up -d quake3
docker compose down quake3

Run the phpDocumentor service (separately)

* As the code generation is only run occassionally, the phpDoc service (phpdoc) is separated from the general services.

docker compose --profile docu up
# exits automatically

🏷️ Docker services -> profiles mapping

The docker-compose.yml file uses Docker Service-profiles to group services into logical groups.

  • This allows to only start / stop a certain group of services at once.
  • Yet individual docker services can still be targeted individually by referencing their service name.

Some single services have their own profile, in order to prevent them from starting/stopping when using docker compose without any --profile.

Tip

Multiple profiles can be combined: docker compose --profile webserver --profile irc up -d

Profile Applicablae Docker Services Example Usage
all All general services --profile all
setup sslcerts postfix-smtp --profile setup
status servicealerts dashboard reverseproxy waf waf-dashboard --profile status
webserver servicealerts dashboard reverseproxy waf waf-dashboard website db db-manager postfix-smtp --profile webserver
irc servicealerts dashboard reverseproxy waf irc irc-quizbot irc-telegram-bridge --profile irc
keepass servicealerts dashboard sftp --profile keepass
quake servicealerts dashboard quake3 --profile quake
docu phpdoc --profile docu
Single service e.g. stockticker docker compose up -d stockticker


🩺 Docker resource usage & services health

Quick resource analysis using the CLI

This is particularly helpful to fine-tune the CPU & memory limits for the Docker services, which can be adjusted in the .env-file.

docker stats
Example docker status output
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}"

NAME                   CPU %     MEM USAGE / LIMIT     MEM %
zorg-reverseproxy      0.00%     69.01MiB / 1GiB       6.74%
zorg-stockticker       0.03%     70.19MiB / 1GiB       6.85%
zorg-mariadb           0.01%     133.8MiB / 4GiB       3.27%
zorg-website           0.01%     8.855MiB / 4GiB       0.22%
zorg-dashboard         0.00%     27.06MiB / 1GiB       2.64%
zorg-mailserver        0.11%     39.41MiB / 256MiB    15.39%

The Docker Status-Dashboard

The full-fledged Docker Management Dashboard (Portainer) can be accessed at:

  • https://dockerstatus.DOMAINNAME
    Host can be adjusted in the .env

WAF inspection

Web UI Dashboard

The WAF Web UI (alerts, decisions, metrics) can be accessed at:

  • https://dockerstatus.DOMAINNAME/waf
    Shares the DASHBOARD_HOST subdomain with Portainer β€” path prefix /waf is handled by BASE_PATH

Important

The WAF dashboard is protected by the IP whitelist middleware. Access requires your IP to be listed in ip-whitelist in resources/reverseproxy/middlewares-http.yaml.

Note

First-time setup: On first launch the web UI will prompt for CrowdSec LAPI connection details. Use machine ID crowdsec-web-ui and the value of CROWDSEC_WEBUI_KEY from your .env. See initial setup if you haven't registered the machine yet.

CLI metrics

Quick overview of parsed log lines, triggered scenarios, active decisions and bouncer activity:

docker compose exec zorg-waf cscli metrics
Additional CrowdSec WAF cscli commands
# List active bans/decisions
docker compose exec waf cscli decisions list
# List triggered alerts
docker compose exec waf cscli alerts list
# List registered bouncers (e.g. traefik)
docker compose exec waf cscli bouncers list
# List registered watcher machines (e.g. waf-dashboard)
docker compose exec waf cscli machines list


πŸ†™ Update all Docker images

cd into the directory containing the docker-compose.yml (symlink), and run this shell command:

Tip

The images can be scoped to update only services within a specific Docker services profile

cd /srv/<my-website>/<host>/
for image in $(docker compose --profile all config | awk '/image:/ { print $2 }'); do docker pull "$image"; done;

Alternatively, use the docker-update.sh script (can also be run via Host's cron):

cd /srv/<my-website>/<host>/
./docker-update.sh

Caution

Updating Docker images will NOT renew running services - they have to be rebuilt!



πŸ‘¨β€πŸ« Explanations

πŸ§ͺ Debugging Docker Services

For DEBUGGING mode – with an interactive log output to the active shell - omit the -d flag when starting services:

docker compose --file ./website/docker-compose.yml up <-- no -d flag


πŸ”₯ Firewall ports configuration

Tip

Docker circumvents the Host machine's firewall – so usually NO need (or not possible) to configure the Host machine's firewall!

Ensure the Host machine's firewall is configured to expose & allow access through the required ports for different Docker Services:

Allow a port - or port range

A non-conclusive, depends on what ports: are set in the .env file.

[!IMPORTANT] Do NOT expose the default Database port 3306 to the world-wide-web!

sudo ufw allow 80 # webserver/reverseproxy http
sudo ufw allow 443 # webserver/reverseproxy https
sudo ufw allow 9443/tcp # Docker dashboard (secure)
sudo ufw allow 6667/tcp # irc-Server
sudo ufw allow 6697/tcp # irc-Server (secure)
sudo ufw allow 2222/tcp # ftp-Server | NOTE: 22 reserved for ssh
sudo ufw allow 27960/udp # quake3-Server
Inspect all rules - i.e. allowed ports
% sudo ufw status

Status: active

To                         Action      From
--                         ------      ----
80                         ALLOW       Anywhere
443                        ALLOW       Anywhere
587                        ALLOW       Anywhere
6667/tcp                   ALLOW       Anywhere
6697/tcp                   ALLOW       Anywhere
2222/tcp                   ALLOW       Anywhere
27960/udp                  ALLOW       Anywhere
80 (v6)                    ALLOW       Anywhere (v6)
443 (v6)                   ALLOW       Anywhere (v6)
587 (v6)                   ALLOW       Anywhere (v6)
6667/tcp (v6)              ALLOW       Anywhere (v6)
6697/tcp (v6)              ALLOW       Anywhere (v6)
2222/tcp (v6)              ALLOW       Anywhere (v6)
27960/udp (v6)             ALLOW       Anywhere (v6)

πŸ“„ The /zorg-docker/resources-directory & files

Contains site specific resources that are actively mapped from the Host to some of the Docker Services. But it also contains some example files that can be used to configure the services.

Examples of example files
  • irc/ircd.yaml β†’ copy to HOST_PATH_IRC_IRCD_CONFIG_FILES, then add opers: block with bcrypt hashes
  • website/apache/example.conf --> use as apache.conf
  • website/php/example.crontab --> use as crontab
  • website/sendmail/example-msmtprc --> use as msmtprc
  • quake3/example-autoexec.cfg --> use as autoexec.cfg

πŸ” logrotate must be done on the Host

The Docker services are just writing logs to the mapped /logs-directory, but logrotate must be configured on the Host machine.


πŸ’Ώ Import/export SQL-dumps with MariaDB

A third-party SQL Manager (e.g. on macOS use SequelAce) or CLI application is required to connect to the MariaDB service under the specified host and port.

Import an SQL dump

mysql -h <db.host.domain> -P 3306 -u MYSQL_USER -p MYSQL_DATABASE < /path/to/import-dump.sql

Export an SQL dump via CLI

mysqldump -h <db.host.domain> -P 3306 -u MYSQL_USER -p MYSQL_DATABASE > /path/to/save-dump.sql





βš–οΈ License

Copyright (C) 2024-2026 zorg Verein https://github.com/zorgch

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.

This program comes with ABSOLUTELY NO WARRANTY; for details read the README. This is free software, and you are welcome to redistribute it under certain conditions; see the LICENSE.




About

Docker setup to run the zorg.ch Website and its related services in an isolated, server-independent environment.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Contributors