Custom SSH Provider
The Custom SSH provider lets you manage any server with SSH access, including on-premise servers, dedicated servers, or VMs on unsupported cloud providers.
How It Works
NightOps connects via SSH and executes your custom commands:
NightOps → SSH → Your Server → Execute Command
You define:
- Start command: What runs when the schedule starts
- Stop command: What runs when the schedule stops
- Status command: How to check if the service is running
Use Cases
| Scenario | Start Command | Stop Command |
|---|---|---|
| Docker containers | docker start myapp | docker stop myapp |
| Systemd services | systemctl start myapp | systemctl stop myapp |
| PM2 processes | pm2 start myapp | pm2 stop myapp |
| Custom scripts | /opt/scripts/start.sh | /opt/scripts/stop.sh |
| VM management | virsh start myvm | virsh shutdown myvm |
Setup
Step 1: Create SSH Key Pair
Generate a dedicated key pair for NightOps:
ssh-keygen -t ed25519 -C "nightops" -f ~/.ssh/nightops
This creates:
~/.ssh/nightops— Private key (add to NightOps)~/.ssh/nightops.pub— Public key (add to server)
Step 2: Add Public Key to Server
On your target server:
# Create nightops user (recommended)
sudo useradd -m -s /bin/bash nightops
# Add public key
sudo mkdir -p /home/nightops/.ssh
sudo cat >> /home/nightops/.ssh/authorized_keys << 'EOF'
<paste contents of ~/.ssh/nightops.pub here>
EOF
sudo chown -R nightops:nightops /home/nightops/.ssh
sudo chmod 700 /home/nightops/.ssh
sudo chmod 600 /home/nightops/.ssh/authorized_keys
Step 3: Configure Sudo (if needed)
If your commands require root privileges:
# /etc/sudoers.d/nightops
nightops ALL=(ALL) NOPASSWD: /usr/bin/systemctl start myapp
nightops ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop myapp
nightops ALL=(ALL) NOPASSWD: /usr/bin/systemctl status myapp
Step 4: Add Provider in NightOps
- Go to Providers → Add Provider → Custom SSH
- Configure connection:
- Host: Your server's hostname or IP
- Port: SSH port (default: 22)
- Username:
nightops(or your user) - Private Key: Paste contents of
~/.ssh/nightops
- Configure commands:
- Start Command: Command to start your service
- Stop Command: Command to stop your service
- Status Command: Command to check status (exit 0 = running)
- Click Test Connection
- Click Save Provider
Command Examples
Docker Compose
Start: cd /app && docker compose up -d
Stop: cd /app && docker compose down
Status: cd /app && docker compose ps --quiet | grep -q .
Systemd Service
Start: sudo systemctl start myapp.service
Stop: sudo systemctl stop myapp.service
Status: systemctl is-active --quiet myapp.service
Multiple Services
Start: sudo systemctl start nginx && sudo systemctl start api
Stop: sudo systemctl stop api && sudo systemctl stop nginx
Status: systemctl is-active --quiet nginx && systemctl is-active --quiet api
Custom Script
# /opt/nightops/start.sh
#!/bin/bash
cd /app
source venv/bin/activate
python manage.py runserver &
echo $! > /var/run/myapp.pid
Start: /opt/nightops/start.sh
Stop: kill $(cat /var/run/myapp.pid) && rm /var/run/myapp.pid
Status: test -f /var/run/myapp.pid && kill -0 $(cat /var/run/myapp.pid) 2>/dev/null
Status Command
The status command determines whether the service is running:
- Exit code 0 → Running (shown as green in NightOps)
- Non-zero exit → Stopped (shown as red in NightOps)
Examples
# Check if process exists
pgrep -f myapp
# Check systemd service
systemctl is-active --quiet myapp
# Check Docker container
docker inspect -f '{{.State.Running}}' mycontainer | grep -q true
# Check port is listening
nc -z localhost 8080
# Check HTTP endpoint
curl -sf http://localhost:8080/health > /dev/null
Security Best Practices
1. Dedicated User
Create a dedicated nightops user with minimal permissions:
sudo useradd -m -s /bin/bash nightops
2. Restricted SSH Key
Limit what the SSH key can do in authorized_keys:
command="/home/nightops/allowed-commands.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA...
3. Specific Sudo Rules
Only allow the exact commands needed:
nightops ALL=(ALL) NOPASSWD: /usr/bin/systemctl start myapp
nightops ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop myapp
4. Firewall
Restrict SSH access to NightOps IP ranges if possible.
Troubleshooting
"Connection Refused"
- Verify SSH is running on the target server
- Check the port is correct
- Verify firewall allows the connection
"Permission Denied"
- Verify the public key is in
authorized_keys - Check file permissions (700 for
.ssh, 600 forauthorized_keys) - Verify the private key in NightOps is correct
"Command Failed"
- Test the command manually via SSH
- Check if sudo is required and configured
- Verify the command path is correct
Status Always Shows "Running" or "Stopped"
- Verify the status command exit code:
ssh nightops@server 'your-status-command'; echo "Exit: $?" - Exit 0 = running, non-zero = stopped
Limitations
- No auto-discovery: Assets must be manually defined
- No cost tracking: NightOps can't determine costs for custom resources
- SSH access required: The server must be reachable via SSH