Portable, Server independent, Docker-based code to get the zorg Websites and Services up, running, and hosted.
Table of Contents
- git installation
- Docker installation
- π Folder structure setup
- πΎ Docker images
- 𧬠Docker networking
- Manage general services
- π·οΈ Docker services -> profiles mapping
- π©Ί Resource usage & services health
- π Update all Docker images
- π§ͺ Debugging Docker Services
- π₯ Firewall ports configuration
- π The
/zorg-docker/resources-directory & files - π logrotate handling
- πΏ Import/export SQL-dumps with MariaDB
Install git for your OS.
Following the official installation instructions for Docker.
Tip
On Ubuntu it's advised against installing via snap, as this may cause compatibility issues!
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.nnOn 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.devIn general make sure to work from the project root directory:
cd /srv/<my-website>/<host>
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/
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 |
traefikcrowdsec-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 |
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
git clone -b <branch-name> --depth 1 https://github.com/zorgch/zorg-docker.git ./zorg-dockerNote
See below section for how to UPDATE the cloned git repository to get its latest changes.
cp ./zorg-docker/.env.example ./.envImportant
Using your text editor of choice, adjust the .env-file to the setup of your host machine.
ln -s ./zorg-docker/docker-compose.yml ./docker-compose.ymlThese 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-udpNote
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!
docker compose buildcd into the directory containing the locally cloned git files, and run a git pull:
cd /srv/<my-website>/<host>/zorg-docker
git pull --rebaseImportant
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(-)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 upGenerate a password, set it in .env as CROWDSEC_WEBUI_KEY, then register it on CrowdSec:
- Generate & save password into .env
openssl rand -hex 32
# β set as CROWDSEC_WEBUI_KEY in .env- 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/nullNote
This requires the mailserver postfix-smtp service to be running!
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>
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
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 genpasswdThen add opers: to the ircd.yaml config-file:
opers:
nickname:
class: "server-admin"
password: "<hash-from-genpasswd>"
modes: +isConnect 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>
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
Connect to the IRC server, and oper yourself:
/OPER <nickname> <password>
/msg NickServ SAREGISTER quizhans <password>
/msg NickServ SAREGISTER telegram <password>
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
* 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* 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* As the code generation is only run occassionally, the phpDoc service (phpdoc) is separated from the general services.
docker compose --profile docu up
# exits automaticallyThe 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 |
This is particularly helpful to fine-tune the CPU & memory limits for the Docker services, which can be adjusted in the .env-file.
docker statsExample 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 full-fledged Docker Management Dashboard (Portainer) can be accessed at:
https://dockerstatus.DOMAINNAME
Host can be adjusted in the.env
The WAF Web UI (alerts, decisions, metrics) can be accessed at:
https://dockerstatus.DOMAINNAME/waf
Shares theDASHBOARD_HOSTsubdomain with Portainer β path prefix/wafis handled byBASE_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.
Quick overview of parsed log lines, triggered scenarios, active decisions and bouncer activity:
docker compose exec zorg-waf cscli metricsAdditional 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 listcd 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.shCaution
Updating Docker images will NOT renew running services - they have to be rebuilt!
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
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
3306to 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-ServerInspect 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)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 toHOST_PATH_IRC_IRCD_CONFIG_FILES, then addopers:block with bcrypt hasheswebsite/apache/example.conf--> use asapache.confwebsite/php/example.crontab--> use ascrontabwebsite/sendmail/example-msmtprc--> use asmsmtprcquake3/example-autoexec.cfg--> use asautoexec.cfg
The Docker services are just writing logs to the mapped /logs-directory, but logrotate must be configured on the Host machine.
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.
mysql -h <db.host.domain> -P 3306 -u MYSQL_USER -p MYSQL_DATABASE < /path/to/import-dump.sqlmysqldump -h <db.host.domain> -P 3306 -u MYSQL_USER -p MYSQL_DATABASE > /path/to/save-dump.sqlCopyright (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.