This page looks best with JavaScript enabled

Deploying VictoriaMetrics in Containers

 ·  ☕ 8 min read

VictoriaMetrics (VM for short) is a time-series database compatible with the Prometheus ecosystem. It uses fewer resources than Prometheus and comes in both single-node and cluster editions. The single-node edition can directly replace Prometheus storage; the cluster edition splits reads, writes, and storage across vmstorage / vminsert / vmselect, giving it stronger horizontal scaling.

1. VictoriaMetrics Single Node

The single-node edition (victoria-metrics) combines ingestion, storage, and querying in one process. It suits small-to-medium monitoring setups and works as a Prometheus remote_write remote storage target.

1.1 Configure Environment Variables

1
2
3
4
5
6
7
export CONTAINER_CLI=nerdctl
export VM_TAG=v1.147.0
export VM_IMAGE=victoriametrics/victoria-metrics:$VM_TAG
export VM_INSTANCE_NAME=victoriametrics
export VM_DATA=/data/ops/victoriametrics/$VM_INSTANCE_NAME
export VM_HTTP_PORT=8428
export VM_RETENTION=15d

1.2 Start the Service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
mkdir -p $VM_DATA/data

$CONTAINER_CLI run -d \
  --name $VM_INSTANCE_NAME \
  --restart always \
  --security-opt apparmor=unconfined \
  --security-opt seccomp=unconfined \
  --network host \
  --ulimit nofile=1048576:1048576 \
  --memory-swappiness=0 \
  -v $VM_DATA/data:/victoria-metrics-data \
  $VM_IMAGE \
  -storageDataPath=/victoria-metrics-data \
  -retentionPeriod=$VM_RETENTION \
  -httpListenAddr=:$VM_HTTP_PORT

1.3 Verify

1
2
3
4
5
6
7
8
# Health check
curl -s http://127.0.0.1:$VM_HTTP_PORT/health

# Inspect its own metrics
curl -s http://127.0.0.1:$VM_HTTP_PORT/metrics | head

# PromQL query (compatible with the Prometheus API)
curl -s 'http://127.0.0.1:8428/api/v1/query?query=up'

Write test (Prometheus remote_write format):

1
2
3
curl -s -X POST http://127.0.0.1:$VM_HTTP_PORT/api/v1/import/prometheus -d '
vm_test_metric{env="dev"} 1
'

1.4 Print the Delivery Result

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
cat <<EOF
VictoriaMetrics (single):
  Instance Name:  $VM_INSTANCE_NAME
  IP:             $(hostname -I | awk '{print $1}')
  HTTP Port:      $VM_HTTP_PORT
  Retention:      $VM_RETENTION
  Write URL:      http://$(hostname -I | awk '{print $1}'):$VM_HTTP_PORT/api/v1/write
  Query URL:      http://$(hostname -I | awk '{print $1}'):$VM_HTTP_PORT/api/v1/query
  Metrics URL:    http://$(hostname -I | awk '{print $1}'):$VM_HTTP_PORT/metrics
EOF

2. VictoriaMetrics Cluster

The example below uses 3 vmstorage nodes + vminsert / vmselect running on each. This is the minimal production topology for the cluster edition: vmstorage stores data, vminsert handles write routing, and vmselect handles query aggregation.

NodeIP (example)Role
node110.0.0.1vmstorage + vminsert + vmselect
node210.0.0.2vmstorage + vminsert + vmselect
node310.0.0.3vmstorage + vminsert + vmselect

All three machines must be able to reach each other on the following ports:

PortComponentPurpose
8400vmstorageReceives writes from vminsert (TCP)
8401vmstorageReceives queries from vmselect (TCP)
8480vminsertHTTP writes / remote_write
8481vmselectHTTP queries / Grafana data source
8482vmstorageHTTP monitoring /metrics

2.1 Configure Environment Variables on Each Node

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
export CONTAINER_CLI=nerdctl
export VM_TAG=v1.147.0-cluster
export VM_CLUSTER_NAME=vm_cluster_3n

export VM_STORAGE_IMAGE=victoriametrics/vmstorage:$VM_TAG
export VM_INSERT_IMAGE=victoriametrics/vminsert:$VM_TAG
export VM_SELECT_IMAGE=victoriametrics/vmselect:$VM_TAG

export VM_NODE1_IP=10.0.0.1
export VM_NODE2_IP=10.0.0.2
export VM_NODE3_IP=10.0.0.3

export NODE_ID=1                              # node2 uses 2, node3 uses 3
export VM_NODE_IP=$VM_NODE1_IP                # local IP: node1/2/3 fill in the matching variable

export VM_STORAGE_PORT=8400
export VM_SELECT_PORT=8401
export VM_INSERT_HTTP_PORT=8480
export VM_SELECT_HTTP_PORT=8481
export VM_STORAGE_HTTP_PORT=8482
export VM_RETENTION=15d
export VM_REPLICATION_FACTOR=2                # replication factor, no greater than the number of vmstorage nodes

export VM_DATA=/data/ops/victoriametrics/$VM_CLUSTER_NAME-node$NODE_ID
export VM_STORAGE_NODES="$VM_NODE1_IP:$VM_STORAGE_PORT,$VM_NODE2_IP:$VM_STORAGE_PORT,$VM_NODE3_IP:$VM_STORAGE_PORT"
export VM_SELECT_NODES="$VM_NODE1_IP:$VM_SELECT_PORT,$VM_NODE2_IP:$VM_SELECT_PORT,$VM_NODE3_IP:$VM_SELECT_PORT"

Check whether the ports are in use:

1
lsof -i :$VM_STORAGE_PORT -i :$VM_SELECT_PORT -i :$VM_INSERT_HTTP_PORT -i :$VM_SELECT_HTTP_PORT -i :$VM_STORAGE_HTTP_PORT

2.2 Start vmstorage

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
mkdir -p $VM_DATA/vmstorage

$CONTAINER_CLI run -d \
  --name vmstorage-$VM_CLUSTER_NAME-$NODE_ID \
  --restart always \
  --security-opt apparmor=unconfined \
  --security-opt seccomp=unconfined \
  --network host \
  --ulimit nofile=1048576:1048576 \
  --memory-swappiness=0 \
  -v $VM_DATA/vmstorage:/storage \
  $VM_STORAGE_IMAGE \
  -storageDataPath=/storage \
  -retentionPeriod=$VM_RETENTION \
  -vminsertAddr=:$VM_STORAGE_PORT \
  -vmselectAddr=:$VM_SELECT_PORT \
  -httpListenAddr=:$VM_STORAGE_HTTP_PORT

2.3 Start vminsert

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$CONTAINER_CLI run -d \
  --name vminsert-$VM_CLUSTER_NAME-$NODE_ID \
  --restart always \
  --security-opt apparmor=unconfined \
  --security-opt seccomp=unconfined \
  --network host \
  --ulimit nofile=1048576:1048576 \
  --memory-swappiness=0 \
  $VM_INSERT_IMAGE \
  -storageNode=$VM_STORAGE_NODES \
  -replicationFactor=$VM_REPLICATION_FACTOR \
  -httpListenAddr=:$VM_INSERT_HTTP_PORT

2.4 Start vmselect

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$CONTAINER_CLI run -d \
  --name vmselect-$VM_CLUSTER_NAME-$NODE_ID \
  --restart always \
  --security-opt apparmor=unconfined \
  --security-opt seccomp=unconfined \
  --network host \
  --ulimit nofile=1048576:1048576 \
  --memory-swappiness=0 \
  $VM_SELECT_IMAGE \
  -storageNode=$VM_SELECT_NODES \
  -httpListenAddr=:$VM_SELECT_HTTP_PORT

2.5 Verify the Cluster

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# vmstorage metrics
curl -s http://$VM_NODE_IP:$VM_STORAGE_HTTP_PORT/metrics | grep vm_rows | head

# Write through vminsert
curl -s -X POST "http://$VM_NODE_IP:$VM_INSERT_HTTP_PORT/insert/0/prometheus/api/v1/import/prometheus" -d '
vm_cluster_test{node="'"$NODE_ID"'"} 1
'

# Query through vmselect
curl -s "http://$VM_NODE_IP:$VM_SELECT_HTTP_PORT/select/0/prometheus/api/v1/query?query=vm_cluster_test"

Check whether every vmstorage node is seen by vminsert / vmselect:

1
2
curl -s http://$VM_NODE_IP:$VM_INSERT_HTTP_PORT/metrics | grep vm_rpc_vmstorage_is_reachable
curl -s http://$VM_NODE_IP:$VM_SELECT_HTTP_PORT/metrics | grep vm_rpc_vmstorage_is_reachable

A value of 1 for vm_rpc_vmstorage_is_reachable means connectivity is fine.

2.6 Print the Delivery Result

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
cat <<EOF
VictoriaMetrics Cluster:
  Cluster Name:       $VM_CLUSTER_NAME
  Node ID:            $NODE_ID
  Node IPs:
    node1:            $VM_NODE1_IP
    node2:            $VM_NODE2_IP
    node3:            $VM_NODE3_IP
  Retention:          $VM_RETENTION
  Replication Factor: $VM_REPLICATION_FACTOR
  Write URLs (any vminsert):
    http://$VM_NODE1_IP:$VM_INSERT_HTTP_PORT/insert/0/prometheus/api/v1/write
    http://$VM_NODE2_IP:$VM_INSERT_HTTP_PORT/insert/0/prometheus/api/v1/write
    http://$VM_NODE3_IP:$VM_INSERT_HTTP_PORT/insert/0/prometheus/api/v1/write
  Query URLs (any vmselect):
    http://$VM_NODE1_IP:$VM_SELECT_HTTP_PORT/select/0/prometheus/
    http://$VM_NODE2_IP:$VM_SELECT_HTTP_PORT/select/0/prometheus/
    http://$VM_NODE3_IP:$VM_SELECT_HTTP_PORT/select/0/prometheus/
EOF

3. vmagent Scrape Configuration

vmagent scrapes Exporter metrics and remote_writes them to VM. It can replace the scraping side of Prometheus. Point it at vmsingle for the single-node edition; point it at any vminsert for the cluster edition.

3.1 Configure Environment Variables

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
export CONTAINER_CLI=nerdctl
export VM_TAG=v1.147.0
export VM_AGENT_IMAGE=victoriametrics/vmagent:$VM_TAG
export VM_AGENT_NAME=vmagent
export VM_AGENT_DATA=/data/ops/victoriametrics/$VM_AGENT_NAME
export VM_AGENT_HTTP_PORT=8429

# Single-node edition
export VM_REMOTE_WRITE_URL=http://127.0.0.1:8428/api/v1/write

# Cluster edition (switch to a vminsert address)
export VM_REMOTE_WRITE_URL=http://10.0.0.1:8480/insert/0/prometheus/api/v1/write

3.2 Generate the Scrape Configuration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
mkdir -p $VM_AGENT_DATA

cat > $VM_AGENT_DATA/scrape.yml <<'EOF'
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: node-exporter
    static_configs:
      - targets:
          - 10.0.0.1:9100
EOF

3.3 Start vmagent

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$CONTAINER_CLI run -d \
  --name $VM_AGENT_NAME \
  --restart always \
  --security-opt apparmor=unconfined \
  --security-opt seccomp=unconfined \
  --network host \
  --ulimit nofile=1048576:1048576 \
  --memory-swappiness=0 \
  -v $VM_AGENT_DATA/scrape.yml:/etc/vmagent/scrape.yml \
  $VM_AGENT_IMAGE \
  -promscrape.config=/etc/vmagent/scrape.yml \
  -remoteWrite.url=$VM_REMOTE_WRITE_URL \
  -httpListenAddr=:$VM_AGENT_HTTP_PORT

3.4 Verify

1
2
curl -s http://127.0.0.1:$VM_AGENT_HTTP_PORT/metrics | grep vmagent_remotewrite_rows_sent_total
curl -s http://127.0.0.1:$VM_AGENT_HTTP_PORT/targets

4. Self-Monitoring

Every VM component exposes /metrics. It is best to have vmagent scrape them all and write them into the cluster.

4.1 Scrape Configuration

Have vmagent scrape the VM components and node-exporter together, and write them into the cluster via remote_write. This replaces the configuration from section 3.2 and covers the components on all three nodes:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
export VM_AGENT_DATA=/data/ops/victoriametrics/vmagent
mkdir -p $VM_AGENT_DATA

cat > $VM_AGENT_DATA/scrape.yml <<'EOF'
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: vmstorage
    static_configs:
      - targets:
          - 10.0.0.1:8482
          - 10.0.0.2:8482
          - 10.0.0.3:8482
        labels:
          cluster: vm_cluster_3n
          component: vmstorage

  - job_name: vminsert
    static_configs:
      - targets:
          - 10.0.0.1:8480
          - 10.0.0.2:8480
          - 10.0.0.3:8480
        labels:
          cluster: vm_cluster_3n
          component: vminsert

  - job_name: vmselect
    static_configs:
      - targets:
          - 10.0.0.1:8481
          - 10.0.0.2:8481
          - 10.0.0.3:8481
        labels:
          cluster: vm_cluster_3n
          component: vmselect

  - job_name: vmagent
    static_configs:
      - targets:
          - 127.0.0.1:8429
        labels:
          cluster: vm_cluster_3n
          component: vmagent

  - job_name: node-exporter
    static_configs:
      - targets:
          - 10.0.0.1:9100
          - 10.0.0.2:9100
          - 10.0.0.3:9100
EOF

For the single-node edition, change scrape_configs to a vmsingle job (127.0.0.1:8428) and drop vmstorage / vminsert / vmselect.

Restart vmagent after the change:

1
$CONTAINER_CLI restart $VM_AGENT_NAME

4.2 Verify Scraping

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# vmagent target status (State should be up)
curl -s http://127.0.0.1:8429/targets | grep -E 'vmstorage|vminsert|vmselect|vmagent'

# Sample metrics from each component
curl -s http://10.0.0.1:8482/metrics | grep -E '^vm_rows|^vm_active_merges' | head
curl -s http://10.0.0.1:8480/metrics | grep vm_rpc_vmstorage_is_reachable
curl -s http://10.0.0.1:8481/metrics | grep vm_cache_requests_total | head

# Check through vmselect whether the self-monitoring metrics made it into storage
curl -s 'http://10.0.0.1:8481/select/0/prometheus/api/v1/query?query=up{component="vmstorage"}'

Key metrics to watch:

MetricMeaning
vm_rowsCurrent number of time-series rows in vmstorage
vm_rpc_vmstorage_is_reachableWhether vminsert/vmselect can reach vmstorage (1 = healthy)
vmagent_remotewrite_rows_sent_totalNumber of rows vmagent wrote successfully
vm_http_requests_totalHTTP request volume per component
process_resident_memory_bytesProcess memory usage

4.3 Import Grafana Dashboards

Use the Prometheus-type data source configured in section 5.1 (pointing at vmselect).

In Grafana, go to Dashboards → New → Import on the left and enter a dashboard ID to import:

DashboardIDUseDescription
VictoriaMetrics - cluster11176Cluster editionvmstorage / vminsert / vmselect RPC, cache, resources
VictoriaMetrics - vmagent12683vmagentScrape targets, remote_write rate and errors
VictoriaMetrics - single-node10229Single-node editionvmsingle writes, queries, storage, resources

Import steps:

  1. Enter the ID (e.g. 11176) → Load
  2. For the VictoriaMetrics data source, pick the one created in section 5.1 (for a cluster, enter the vmselect URL)
  3. Import

For the cluster edition, import both 11176 (components) and 12683 (vmagent); for the single-node edition, import 10229.

In the dashboard, filter by the instance or component label to view the vmstorage status of each of the three nodes separately.

5. Integrating with Grafana / Prometheus

5.1 Grafana Data Source

Deployment ModeURL
Single-nodehttp://<vm-ip>:8428
Clusterhttp://<vmselect-ip>:8481/select/0/prometheus/

Choose Prometheus as the type; it is fine once Save & Test passes.

5.2 remote_write Ingestion

Single-node edition:

1
2
remote_write:
  - url: http://10.0.0.1:8428/api/v1/write

Cluster edition:

1
2
remote_write:
  - url: http://10.0.0.1:8480/insert/0/prometheus/api/v1/write

微信公众号
WRITTEN BY
微信公众号