Deployment
What you’ll need
- A valid domain (e.g.
taskminder.de) - A server (minimum 2GB RAM and 20GB storage) running Ubuntu (≥ 24.04 LTS) with sudo or root access
- The codebase of TaskMinder from https://github.com/TaskMinder/TaskMinder
1. DNS Configuration
Before proceeding with the server setup, configure your domain to point to your Ubuntu server.
Get Your Server’s Public IP Address
On your server, run:
curl ifconfig.me
Copy the returned IP (e.g., 203.0.113.42). Make sure the server is not behind a router. The following guide will use this example IP — replace it with your actual IP.
Configure DNS Records
Go to your domain registrar’s DNS management page (e.g., Namecheap, GoDaddy, Cloudflare etc.) and add the following records:
| Type | Name | Value (replace) | TTL |
|---|---|---|---|
| A | @ | 203.0.113.42 | Automatic / 3600 |
| A | www | 203.0.113.42 | Automatic / 3600 |
This assumes you’re using
example.comand wantwww.example.comto also work.
We also use a subdomain for monitoring (monitoring.example.com) and a subdomain for a status page.
We use https://betterstack.com/ as it offers custom subdomains for the status page, but you may choose another provider. After setting up the status page, follow BetterStack’s instructions to configure the CNAME record.
For the monitoring page (monitoring.example.com), add the following record:
| Type | Name | Value (replace) |
|---|---|---|
| A | monitoring | 203.0.113.42 |
Wait for Propagation
DNS propagation can take a few minutes to several hours. Use tools like:
Once your domain resolves to your server’s IP, proceed to the next step.
2. Install Required Packages
Update and install dependencies:
This installs (if not already installed) Git, curl, NGINX, libnginx-mod-http-lua (for Lua in NGINX), lua-cjson (for NGINX), UFW, and Fail2Ban:
sudo apt update && sudo apt upgrade -y
sudo apt install -y git curl nginx ufw fail2ban libnginx-mod-http-lua lua-cjson
Verify lua was installed and is enabled:
See if the module was auto-enabled, if not, enable it first before proceeding:
ls /etc/nginx/modules-enabled/ | grep lua
Install Docker and Docker Compose:
Follow the official Docker documentation to install Docker and Docker Compose:
🔗 https://docs.docker.com/engine/install/ubuntu/
Enable and start Docker
sudo systemctl enable docker
sudo systemctl start docker
Enable and configure firewall (UFW)
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
Configure Fail2Ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
Check status:
sudo fail2ban-client status
3. Clone the Project from GitHub
cd /opt
sudo git clone https://github.com/TaskMinder/TaskMinder.git
cd TaskMinder
4. Configure NGINX
First, modify the nginx.config file to replace taskminder.de with your actual domain name.
vi nginx.config
# or
nano nginx.config
Install Certbot and Obtain SSL Certificates
Install Certbot and its NGINX plugin:
sudo apt install -y certbot python3-certbot-nginx
Run Certbot to obtain SSL certificates (replace example.com and subdomains with your actual domains):
sudo certbot -d example.com -d www.example.com -d monitoring.example.com
Certbot will automatically update the configuration file at /etc/nginx/sites-available/default. Delete this file, as you’ll be using your custom config instead. Also, delete the symlink: sudo rm /etc/nginx/sites-enabled/default
Now that you know the location and filenames of the generated certificates, update your original nginx.config at /opt/TaskMinder/nginx.config. Replace the certificate paths with the correct ones provided by Certbot.
Add Gzip and Lua settings in general nginx.config
Open the main nginx configuration file:
sudo nano /etc/nginx/nginx.conf
Inside the http { } block, comment out all related gzip lines, as we will be using it for compression work. Furthermore, add this line in the http block:
##
# Lua Maintenance Flag Setting
##
lua_shared_dict maintenance_flag 1m;
Deploy Your Final NGINX Configuration
# Copy your updated configuration
sudo cp /opt/TaskMinder/nginx.config /etc/nginx/sites-available/taskminder
# Enable and test the configuration
sudo ln -s /etc/nginx/sites-available/taskminder /etc/nginx/sites-enabled/
sudo nginx -t
# Restart NGINX to apply changes
sudo systemctl restart nginx
5. Set Up Non-Root User
Running as root means a compromised container could gain full system access. It is highly recommended to run the server as a non-root user (least privilege). We’ll use the name ubuntu for this guide, but you may choose another name.
sudo adduser ubuntu
sudo usermod -aG sudo ubuntu
sudo usermod -aG docker ubuntu
If adduser fails, the user likely already exists (common on cloud providers like AWS or DigitalOcean). In that case, just add them to the docker group:
sudo usermod -aG docker ubuntu
Give the user access to the project folder:
sudo chown -R ubuntu:ubuntu /opt/TaskMinder
Log out and reconnect as the ubuntu user:
exit
ssh ubuntu@<your-ip-address>
For enhanced security, use SSH key-based authentication instead of password-based logins to reduce the risk of unauthorized access. After adding your SSH key and verifying the connection, disable both root logins and password authentication:
Edit and/or uncomment the following lines in /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
Then restart SSH:
sudo systemctl restart ssh
6. Automated Backup Setup (via Cron)
Make the Backup Script Executable
The cron service needs permission to run the script. You only need to do this once.
chmod +x /opt/TaskMinder/backup-cmds/db_backup.sh
Add the Job to Crontab
This will schedule the script to run automatically.
Open the crontab editor for the current user:
crontab -e
Add the following line to the bottom of the file, then save and exit:
0 * * * * /opt/TaskMinder/backup-cmds/db_backup.sh >> /var/log/backup.log 2>&1
Verify the Setup
Confirm the job was added successfully by listing the active cron jobs:
crontab -l
You should see the line you just added.
7. Add Docker Secrets and .env.production
Navigate back to the TaskMinder folder and create directories for secrets and backups:
cd /opt/TaskMinder
mkdir docker_secrets
mkdir db-backups
Before starting the application, create the following text files inside the docker_secrets/ folder. These files are used as Docker secrets for configuration:
| Filename | Description |
|---|---|
db_name.txt | Name of the PostgreSQL database. |
db_password.txt | Password for the PostgreSQL database user. |
db_host.txt | Host for the database, usually postgres when running in docker. |
db_user.txt | PostgreSQL database username. |
redis_port.txt | Redis port (default is 6379). |
session_secret.txt | Secure session secret (e.g., generate one with openssl rand -base64 32). |
database_url.txt | Provides the database URL for Prisma ORM: postgresql://db_user:db_password@taskminder-postgres:5432/db_name |
encryption_key.txt | Encryption key for server-side encryption in the database, generated with openssl rand -base64 32 |
encryption_key_secondary.txt | Rotation key for server-side encryption, generated with openssl rand -base64 32 |
encryption_key_lookup.txt | Lookup key for hashes for server-side encryption, generated with openssl rand -base64 32 |
proxy_hop.txt | Proxy hop count for additional reverse proxies that are configured by the server provider. Add 1 to account for the NGINX config. |
8. Run Docker Compose and reset git changes
Navigate to the project root and build/start the containers:
cd /opt/TaskMinder
docker compose up -d --build
Reset git changes:
git reset --hard
And build/start the containers again:
docker compose up -d --build
9. TaskMinder Deployment Complete
Your TaskMinder server should now be running at:
10. What’s Next?
- Create an account to set up a class and add your subjects, teams, and timetable.
- Visit https://monitoring.example.com to change the default password “admin” to a secure one. You’ll be prompted to do this upon your first login.
11. Subsequent Updates
This guide covers minor version upgrades. For major version upgrades, please refer to the relevant migration guides to check for any breaking changes. Before upgrading, enable maintenance mode by adding a file flag with:
touch /etc/nginx/maintenance.flag
-
Navigate to the root folder of the project and stop the Docker Compose process:
docker compose down -
Pull the latest changes from the
mainbranch on GitHub:git pull origin main -
Rebuild and restart the Docker containers:
docker compose up -d --build -
Disable maintenance mode:
rm /etc/nginx/maintenance.flag