This is the definitive “Remote Monitoring” guide. You will have two separate machines communicating with each other.
The Architecture
Server A (Target): Runs your apps + lightweight “Exporters” (agents) that expose metrics.
Server B (Monitoring): Runs Prometheus (database) & Grafana (dashboard). It reaches out to Server A to “scrape” data.
Step 1: Prepare the Target Server (Your App Server)
Do this on the server running your actual applications.
Create a folder:
mkdir -p ~/monitoring-agentCreate a
docker-compose.ymlfile inside it:
YAML
version: '3.8'
services:
node_exporter:
image: quay.io/prometheus/node-exporter:latest
container_name: node_exporter
command:
- '--path.rootfs=/host'
pid: host
restart: unless-stopped
volumes:
- '/:/host:ro,rslave'
ports:
- "9100:9100" # Important: Expose this so Server B can reach it
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
restart: unless-stopped
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
ports:
- "8080:8080" # Important: Expose this so Server B can reach it
devices:
- /dev/kmsg
Run it:
Bash
docker compose up -dFirewall Check: Ensure your cloud provider (AWS/DigitalOcean/Hetzner) firewall allows Incoming Traffic on ports
9100and8080ONLY from the IP address of Server B (your Monitoring server). Do not open these to the whole world.
Step 2: Prepare the Monitoring Server (Your 4GB VPS)
Do this on your separate monitoring machine.
Create a folder structure:
Bash
mkdir -p ~/monitoring/prometheus cd ~/monitoringCreate the
prometheus/prometheus.ymlconfiguration file.Replace
TARGET_SERVER_IPwith the actual IP address of Server A.
YAML
global:
scrape_interval: 15s # How often to check for data
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'remote_server_A'
static_configs:
- targets: ['TARGET_SERVER_IP:9100'] # Node Exporter (Host stats)
labels:
alias: 'App-Server-Main'
- job_name: 'remote_server_A_containers'
static_configs:
- targets: ['TARGET_SERVER_IP:8080'] # cAdvisor (Container stats)
labels:
alias: 'App-Server-Containers'
- Create the
docker-compose.ymlfile in~/monitoring/:
YAML
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
ports:
- "9090:9090"
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
volumes:
- grafana_data:/var/lib/grafana
ports:
- "3000:3000"
volumes:
prometheus_data:
grafana_data:
Run it:
Bash
docker compose up -d
Step 3: Setup the Dashboards
Now the data is flowing, you need to visualize it.
Login: Open
http://YOUR_MONITORING_SERVER_IP:3000.- Default login:
admin/admin.
- Default login:
Connect Data Source:
Go to Connections (or Configuration) -> Data Sources -> Add data source.
Select Prometheus.
URL:
http://prometheus:9090(Keep exactly this; Docker handles the internal DNS).Click “Save & Test”. You should see “Successfully queried the Prometheus API”.
Import Dashboards:
Go to Dashboards -> New -> Import.
We will use standard community IDs (no need to build your own):
Host Stats (Node Exporter): Enter ID
1860and click Load. Select your Prometheus data source and click Import.Container Stats (cAdvisor): Enter ID
14282and click Load.- Note: If 14282 doesn’t work well for you, try ID
193.
- Note: If 14282 doesn’t work well for you, try ID
Verification
You should now see beautiful graphs on your Monitoring Server showing the CPU/RAM usage of your Target Server.
Important Security Note:
Since you are communicating over the public internet, your metrics are technically visible to anyone who guesses the IP and port if you didn’t set up the firewall in Step 1.4.
Best Practice: Use a VPN (like Tailscale or WireGuard) to connect the two servers privately.
Minimum Requirement: Configure
ufwor AWS Security Groups to allow traffic on ports 9100/8080 only from your Monitoring Server’s specific IP.