Skip to content

Bare Metal — Systemd (No Docker)

This guide deploys Dreadnought directly on a Linux host using systemd services — no Docker required. Use this when: - Docker is not available on your system - You prefer not to use containers - You need to integrate with an existing Python/Node process supervisor

This is the most complex deployment method. If Docker is available, consider using Docker Compose instead.


Prerequisites

  • Ubuntu 22.04 / Debian 12 (or similar Linux with systemd)
  • Python 3.12
  • Node.js 18+
  • sudo access

Step 1 — Install System Dependencies

sudo apt update
sudo apt install -y \
  python3.12 \
  python3.12-venv \
  python3-pip \
  nodejs \
  npm \
  sqlite3 \
  git

Verify versions:

python3.12 --version   # Should be 3.12.x
node --version          # Should be 18.x or higher
npm --version

Installing Node.js 18+ on Ubuntu (if the default is too old):

curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs

Step 2 — Create a Dedicated User and Directories

Run all services under a dedicated non-root user for security:

# Create dreadnought system user (no login shell, no home directory prompt)
sudo useradd -r -s /bin/false -d /opt/dreadnought dreadnought

# Create application directories
sudo mkdir -p /opt/dreadnought/backend
sudo mkdir -p /opt/dreadnought/frontend

# Create data directory (SQLite database lives here)
sudo mkdir -p /var/lib/dreadnought
sudo chown dreadnought:dreadnought /var/lib/dreadnought
sudo chmod 755 /var/lib/dreadnought

Step 3 — Clone and Install the Application

# Clone into a temporary location first
git clone https://github.com/dreadnought-0/Dreadnought-DDNS.git /tmp/dreadnought-src

# Copy backend files
sudo cp -r /tmp/dreadnought-src/backend/* /opt/dreadnought/backend/
sudo chown -R dreadnought:dreadnought /opt/dreadnought/backend

# Copy frontend files
sudo cp -r /tmp/dreadnought-src/frontend/* /opt/dreadnought/frontend/
sudo chown -R dreadnought:dreadnought /opt/dreadnought/frontend

# Clean up
rm -rf /tmp/dreadnought-src

Step 4 — Install Python Dependencies

# Create Python virtual environment
sudo -u dreadnought python3.12 -m venv /opt/dreadnought/backend/venv

# Install dependencies into the venv
sudo -u dreadnought /opt/dreadnought/backend/venv/bin/pip install \
  --no-cache-dir \
  -r /opt/dreadnought/backend/requirements.txt

Step 5 — Build the Frontend

# Install Node dependencies
sudo -u dreadnought npm install --prefix /opt/dreadnought/frontend

# Build the Next.js app (creates .next/standalone output)
sudo -u dreadnought npm run build --prefix /opt/dreadnought/frontend

The build creates a standalone server at /opt/dreadnought/frontend/.next/standalone/server.js.


Step 6 — Create the Environment File

sudo nano /etc/dreadnought.env

Add your configuration:

# Cloudflare
CF_API_TOKEN=your_cloudflare_api_token_here

# Admin credentials
ADMIN_EMAIL=you@example.com
ADMIN_PASSWORD=a-strong-password

# Session security — generate with: openssl rand -hex 32
SECRET_KEY=paste-your-64-char-hex-key-here

# Sync settings
POLL_INTERVAL_SECONDS=300
IPV6_ENABLED=false

# Notifications (optional)
DISCORD_WEBHOOK_URL=

# Database (points to the data directory we created)
DATABASE_URL=sqlite:////var/lib/dreadnought/ddns.db

# Timezone
TZ=America/New_York

Secure the file — it contains sensitive values:

sudo chmod 600 /etc/dreadnought.env
sudo chown root:dreadnought /etc/dreadnought.env

Step 7 — Create Systemd Service Files

You need three service files: one for the API, one for the worker, and one for the frontend.

API Service

sudo nano /etc/systemd/system/dreadnought-api.service
[Unit]
Description=Dreadnought DDNS API
Documentation=https://github.com/dreadnought-0/Dreadnought-DDNS
After=network-online.target
Wants=network-online.target

[Service]
Type=exec
User=dreadnought
Group=dreadnought
WorkingDirectory=/opt/dreadnought/backend
EnvironmentFile=/etc/dreadnought.env

ExecStart=/opt/dreadnought/backend/venv/bin/uvicorn \
  main:app \
  --host 127.0.0.1 \
  --port 8000

Restart=on-failure
RestartSec=10
StartLimitInterval=60
StartLimitBurst=3

# Security hardening
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/var/lib/dreadnought
CapabilityBoundingSet=

[Install]
WantedBy=multi-user.target

Worker Service

sudo nano /etc/systemd/system/dreadnought-worker.service
[Unit]
Description=Dreadnought DDNS Worker
Documentation=https://github.com/dreadnought-0/Dreadnought-DDNS
After=network-online.target dreadnought-api.service
Wants=network-online.target
Requires=dreadnought-api.service

[Service]
Type=exec
User=dreadnought
Group=dreadnought
WorkingDirectory=/opt/dreadnought/backend
EnvironmentFile=/etc/dreadnought.env

ExecStart=/opt/dreadnought/backend/venv/bin/python worker.py

Restart=on-failure
RestartSec=10
StartLimitInterval=60
StartLimitBurst=3

NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/var/lib/dreadnought
CapabilityBoundingSet=

[Install]
WantedBy=multi-user.target

Frontend Service

sudo nano /etc/systemd/system/dreadnought-web.service
[Unit]
Description=Dreadnought DDNS Frontend
Documentation=https://github.com/dreadnought-0/Dreadnought-DDNS
After=network-online.target dreadnought-api.service
Wants=network-online.target

[Service]
Type=exec
User=dreadnought
Group=dreadnought
WorkingDirectory=/opt/dreadnought/frontend/.next/standalone

Environment=NODE_ENV=production
Environment=PORT=3000
Environment=HOSTNAME=127.0.0.1
Environment=NEXT_PUBLIC_API_URL=https://ddns-api.yourdomain.com

ExecStart=/usr/bin/node server.js

Restart=on-failure
RestartSec=10
StartLimitInterval=60
StartLimitBurst=3

NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
CapabilityBoundingSet=

[Install]
WantedBy=multi-user.target

Step 8 — Enable and Start Services

# Reload systemd to pick up new service files
sudo systemctl daemon-reload

# Enable services to start at boot
sudo systemctl enable dreadnought-api
sudo systemctl enable dreadnought-worker
sudo systemctl enable dreadnought-web

# Start services
sudo systemctl start dreadnought-api
sudo systemctl start dreadnought-worker
sudo systemctl start dreadnought-web

# Check status of all three
sudo systemctl status dreadnought-api dreadnought-worker dreadnought-web

All three should show active (running).


Step 9 — Set Up a Reverse Proxy

Since the API binds to 127.0.0.1:8000 and the frontend to 127.0.0.1:3000, you need a reverse proxy to expose them publicly. Choose one:


Managing Services

# View logs (live)
sudo journalctl -u dreadnought-api -f
sudo journalctl -u dreadnought-worker -f
sudo journalctl -u dreadnought-web -f

# View recent logs (last 100 lines)
sudo journalctl -u dreadnought-api -n 100

# Restart a service
sudo systemctl restart dreadnought-api

# Stop all
sudo systemctl stop dreadnought-api dreadnought-worker dreadnought-web

# Start all
sudo systemctl start dreadnought-api dreadnought-worker dreadnought-web

Updating (Systemd)

# 1. Stop all services
sudo systemctl stop dreadnought-api dreadnought-worker dreadnought-web

# 2. Pull latest code
cd /tmp
rm -rf dreadnought-src
git clone https://github.com/dreadnought-0/Dreadnought-DDNS.git dreadnought-src

# 3. Update backend files
sudo cp -r /tmp/dreadnought-src/backend/* /opt/dreadnought/backend/
sudo chown -R dreadnought:dreadnought /opt/dreadnought/backend

# 4. Update Python dependencies
sudo -u dreadnought /opt/dreadnought/backend/venv/bin/pip install \
  --no-cache-dir \
  -r /opt/dreadnought/backend/requirements.txt

# 5. Update frontend files
sudo cp -r /tmp/dreadnought-src/frontend/* /opt/dreadnought/frontend/
sudo chown -R dreadnought:dreadnought /opt/dreadnought/frontend

# 6. Rebuild frontend
sudo -u dreadnought npm install --prefix /opt/dreadnought/frontend
sudo -u dreadnought npm run build --prefix /opt/dreadnought/frontend

# 7. Restart services
sudo systemctl start dreadnought-api dreadnought-worker dreadnought-web

# 8. Verify
sudo systemctl status dreadnought-api dreadnought-worker dreadnought-web

Note: The database at /var/lib/dreadnought/ddns.db is never touched during an update. Your data is preserved.


Backup

# Single file backup
sudo -u dreadnought cp /var/lib/dreadnought/ddns.db \
  /var/lib/dreadnought/ddns_$(date +%Y%m%d_%H%M%S).db

# Automated daily backup via cron
sudo crontab -u dreadnought -e
# Add:
0 2 * * * cp /var/lib/dreadnought/ddns.db /var/backups/ddns_$(date +\%Y\%m\%d).db

Troubleshooting Systemd Deployments

"Module not found" or ImportError

The Python venv path might be wrong, or dependencies aren't installed:

sudo -u dreadnought /opt/dreadnought/backend/venv/bin/python -c "import fastapi; print('OK')"

If it fails:

sudo -u dreadnought /opt/dreadnought/backend/venv/bin/pip install -r /opt/dreadnought/backend/requirements.txt

"Cannot open database" error

# Check permissions
ls -la /var/lib/dreadnought/
sudo chown dreadnought:dreadnought /var/lib/dreadnought
sudo chmod 755 /var/lib/dreadnought

Frontend shows blank page or JS errors

The NEXT_PUBLIC_API_URL in the frontend service file may be wrong. Update it to match your actual API URL, then rebuild:

sudo nano /etc/systemd/system/dreadnought-web.service
# Update NEXT_PUBLIC_API_URL

sudo -u dreadnought npm run build --prefix /opt/dreadnought/frontend
sudo systemctl daemon-reload
sudo systemctl restart dreadnought-web

Service exits immediately

Check the logs:

sudo journalctl -u dreadnought-api --since "5 min ago"

Common causes: - Missing or incorrect values in /etc/dreadnought.env - Python version mismatch (needs 3.12) - File permission issues