When deploying a production .NET application alongside Microsoft SQL Server on a self-hosted Ubuntu Linux server using Docker, security and data persistence are top priorities. Out of the box, exposing database ports to the public internet invites aggressive automated botnets and brute-force attacks.
In this guide, we will walk through how to harden your network with iptables, ensure database persistence using Docker volumes, automate regular backups, protect your SSH access with Fail2Ban, and set up a lightweight Telegram alerting system to monitor container health in real time.
1. The Challenge: Combating Database Brute-Force Attacks
When you publish port 1433 (SQL Server) to the public internet for remote management or external integrations, bots scan your IP address almost instantly. Reviewing your container logs (docker logs sqlserver) will often reveal thousands of failed login attempts:
Plaintext
2026-09-30 07:41:00.90 Logon Error: 18456, Severity: 14, State: 8.
2026-09-30 07:41:00.90 Logon Login failed for user 'sa'. Reason: Password did not match that for the login provided. [CLIENT: 130.94.12.51]
To stop this traffic, you need to lock down the network layer before connection requests ever reach the database engine.
2. Hardening Network Security with iptables and Docker
Docker bypasses standard UFW (Uncomplicated Firewall) rules by manipulating iptables directly through the DOCKER-USER chain. We can leverage this chain to block external traffic to port 1433 while explicitly permitting local container communication and trusted management IPs.
Step 1: Configure the DOCKER-USER Chain
Run the following commands on your Ubuntu host to structure your firewall rules:
Bash
# 1. Allow your specific trusted management IP (e.g., your local office/home static IP)
sudo iptables -A DOCKER-USER -p tcp --dport 1433 -s YOUR_MANAGEMENT_IP -j RETURN
# 2. Allow internal Docker bridge networks so your ASP.NET Core web app can talk to SQL Server
sudo iptables -A DOCKER-USER -p tcp --dport 1433 -s 172.16.0.0/12 -j ACCEPT
# 3. Drop all other external public traffic targeting port 1433
sudo iptables -A DOCKER-USER -p tcp --dport 1433 -j DROP
Step 2: Make Firewall Rules Persistent
Ubuntu does not save iptables rules across reboots by default. Install iptables-persistent to lock them in permanently:
Bash
sudo apt update && sudo apt install iptables-persistent -y
sudo netfilter-persistent save
Verify your firewall rules at any time using:
Bash
sudo iptables -L DOCKER-USER -n -v --line-numbers
3. Ensuring Data Persistence with Docker Volumes
Running SQL Server inside a container without a named volume means your data lives in an ephemeral layer that can be lost during container updates.
Step 1: Back Up Your Existing Database
Before making changes, generate a manual backup via your container:
Bash
docker exec -it sqlserver /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P 'YourStrongPassword123!' -C -Q "BACKUP DATABASE [YourDBName] TO DISK = N'/var/opt/mssql/data/backup.bak' WITH FORMAT;"
docker cp sqlserver:/var/opt/mssql/data/backup.bak /home/ubuntu/sql_backups/backup.bak
Step 2: Recreate the Container with a Persistent Volume
Stop and remove the old non-persistent container, create a Docker managed volume, and spin up a fresh instance:
Bash
docker stop sqlserver
docker rm sqlserver
# Create a persistent Docker volume
docker volume create mssql_data
# Deploy the new persistent container
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=YourStrongPassword123!" \
-p 1433:1433 --name sqlserver --restart unless-stopped \
-v mssql_data:/var/opt/mssql \
-d mcr.microsoft.com/mssql/server:2022-latest
Restore your database into the new container using sqlcmd, and restart your ASP.NET Core application container to re-establish the connection pool.
4. Automating Database Backups with Cron
To prevent data loss over time, create an automated backup script (/home/ubuntu/db_backup.sh):
Bash
#!/bin/bash
BACKUP_DIR="/home/ubuntu/sql_backups"
DATE=$(date +%Y%m%d_%H%M%S)
FILENAME="prod_backup_$DATE.bak"
docker exec sqlserver /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P 'YourStrongPassword123!' -C -Q "BACKUP DATABASE [YourDBName] TO DISK = N'/var/opt/mssql/data/$FILENAME';"
docker cp sqlserver:/var/opt/mssql/data/$FILENAME $BACKUP_DIR/$FILENAME
# Optional: Delete backups older than 7 days
find $BACKUP_DIR -name "*.bak" -mtime +7 -delete
Make it executable (chmod +x /home/ubuntu/db_backup.sh) and schedule it in crontab (crontab -e) to run every 6 hours:
Code snippet
0 */6 * * * /home/ubuntu/db_backup.sh >> /home/ubuntu/sql_backups/cron_backup.log 2>&1
5. Securing SSH Access and Installing Fail2Ban
Botnets don't just target databases—they constantly hammer standard SSH ports (22).
Harden SSH Config (
/etc/ssh/sshd_config):Change the default port:
Port 2222Disable root login:
PermitRootLogin noEnforce SSH keys:
PasswordAuthentication no
Restart SSH:
sudo systemctl restart sshInstall Fail2Ban to automatically ban IPs that repeatedly fail authentication:
Bash
sudo apt install fail2ban -y sudo systemctl enable --now fail2ban
6. Building a Lightweight Telegram Monitoring Script
To ensure you are notified immediately if a container goes down or if brute-force attacks surge, create a shell script (/home/ubuntu/system_monitor.sh) that hooks into the Telegram Bot API:
Bash
#!/bin/bash
TOKEN="YOUR_TELEGRAM_BOT_TOKEN"
CHAT_ID="YOUR_CHAT_ID"
MESSAGE=""
# 1. Check if critical containers are down
DOWN_CONTAINERS=$(docker ps -a --format '{{.Names}}: {{.State}}' | grep -E "sqlserver|web-app" | grep -v "running")
if [ -n "$DOWN_CONTAINERS" ]; then
MESSAGE="🚨 *Docker Alert:* Container offline!\n$DOWN_CONTAINERS\n"
fi
# 2. Check for SQL login failures in the last 5 minutes
SQL_FAILS=$(docker logs --since 5m sqlserver 2>&1 | grep -c "Login failed")
if [ "$SQL_FAILS" -gt 5 ]; then
MESSAGE="${MESSAGE}⚠️ *Database Alert:* $SQL_FAILS brute-force login attempts detected!\n"
fi
# Send alert if message is populated
if [ -n "$MESSAGE" ]; then
curl -s -X POST "https://api.telegram.org/bot$TOKEN/sendMessage" \
-d chat_id="$CHAT_ID" \
-d text="$MESSAGE" \
-d parse_mode="Markdown" > /dev/null
fi
Schedule this script in crontab to run every 5 minutes (*/5 * * * * /home/ubuntu/system_monitor.sh), and you will have a proactive, automated notification system safeguarding your infrastructure 24/7.
Conclusion
By combining iptables DOCKER-USER restrictions, persistent Docker volumes, automated cron backups, SSH hardening with Fail2Ban, and Telegram monitoring, your self-hosted .NET application and SQL Server stack will be robust, secure, and ready for production traffic.

Join the conversation! Your thoughts help the community grow.