Dockerize Grafana Self Provisioned Configuration with Dashboards

Sebastian Torralba
AvengaLATAM
Published in
6 min readAug 29, 2023

Sometimes you need to deploy a grafana for your project and configure all the datasources and dashboards on it by hand, using the UI.

In this article I will show, how to deploy an entire grafana, pre configure with self-provisiong datasources and dashboards.

Here is the Repo

The Context

The DashBoard
The Database

There are two components: A grafana with a single dashboard that takes data from a postgreSQL instance. So we move it to a dockerize enviroment.

The Solution Architecture

Let’s start from the beginning:

  1. Create a Dockerfile for Postgres with initial data.
  2. Create a Dockerfile for Grafana, it includes some enviroment variable that allows configure the startup of Grafana and two folders, one is for the dashboards and other the operative configuration, datasources and where the dashboard will be deployed.
  3. Create a docker-compose with the declaration of the services for Grafana and Postgres.

Postgres Dockerfile

FROM postgres:12.15
COPY script/db_init.sql /docker-entrypoint-initdb.d/
ENV POSTGRES_USER=postgres
ENV POSTGRES_PASSWORD=postgres
ENV POSTGRES_DB=testdb
ENV PGDATA=/data

The Init db_init.sql

SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;


ALTER DATABASE testdb OWNER TO postgres;

\connect testdb

SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;

SET default_tablespace = '';
SET schema 'public';
SET default_table_access_method = heap;

CREATE TABLE cars (
car_id SERIAL PRIMARY KEY,
make VARCHAR(50) NOT NULL,
model VARCHAR(50) NOT NULL,
year INT NOT NULL,
color VARCHAR(20),
price NUMERIC(10, 2),
registration_date TIMESTAMP
);

-- Insert data into Car table
INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Toyota', 'Corolla', 2020, 'Silver', 23000.00, '2023-08-07 10:30:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Honda', 'Civic', 2019, 'Red', 22000.00, '2023-08-07 14:20:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Ford', 'Mustang', 2022, 'Blue', 35000.00, '2023-08-08 09:45:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Chevrolet', 'Malibu', 2021, 'White', 27000.00, '2023-08-08 15:10:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Nissan', 'Altima', 2018, 'Black', 19000.00, '2023-08-09 11:00:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('BMW', 'X5', 2023, 'Gray', 60000.00, '2023-08-09 16:30:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Mercedes-Benz', 'C-Class', 2022, 'White', 45000.00, '2023-08-10 12:15:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Audi', 'A4', 2021, 'Blue', 42000.00, '2023-08-10 17:45:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Hyundai', 'Elantra', 2020, 'Silver', 18000.00, '2023-08-11 13:20:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Kia', 'Sorento', 2019, 'Red', 25000.00, '2023-08-11 18:10:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Volkswagen', 'Jetta', 2022, 'Black', 28000.00, '2023-08-12 14:50:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Subaru', 'Outback', 2023, 'Green', 32000.00, '2023-08-12 19:25:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Mazda', 'CX-5', 2021, 'Blue', 26000.00, '2023-08-13 15:40:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Jeep', 'Wrangler', 2020, 'Orange', 35000.00, '2023-08-13 20:15:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Ford', 'Focus', 2023, 'White', 27000.00, '2023-08-14 16:55:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Chevrolet', 'Equinox', 2022, 'Black', 19000.00, '2023-08-14 21:35:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Toyota', 'Rav4', 2019, 'Blue', 32000.00, '2023-08-15 17:30:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Honda', 'Accord', 2021, 'Red', 24000.00, '2023-08-15 22:00:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Nissan', 'Rogue', 2018, 'Silver', 22000.00, '2023-08-16 18:40:00');

INSERT INTO cars (make, model, year, color, price, registration_date)
VALUES ('Ford', 'Escape', 2020, 'Gray', 27000.00, '2023-08-16 23:20:00');

Grafana Dockerfile

FROM grafana/grafana:8.3.7
# ENV Variables for cofiguration
ENV GF_AUTH_ANONYMOUS_ENABLED=true #Enabled the Anonymous user no user/pass needed
ENV GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer #Activate the ROLE VIEWER as Default
ENV GF_AUTH_BASIC_ENABLED=false #Disable the AUTH Method
ENV GF_AUTH_DISABLE_LOGIN_FORM=true #Disable the login on show
ENV GF_AUTH_DISABLE_SIGNOUT_MENU=true #Diabled the menu
ENV GF_SECURITY_ALLOW_EMBEDDING=true #Allow IFRAME Calls
ENV GF_SERVER_SERVE_FROM_SUB_PATH=true
ENV GF_SERVE_FROM_SUB_PATH=true

EXPOSE 3000

ADD ./provisioning /etc/grafana/provisioning #Add the configuration
ADD ./dashboards /var/lib/grafana/dashboards #Add the Dashboard


CMD [ "grafana-reporter" ]

So, now we need to provides all the configuration for grafana

  1. Create the folders for provisioning with the sub-folder dashboards and datasources
  2. Create on the dashboards subfolder the dashboard.yml file.
  3. Create on the datasourcessubfolder the datasource.yml file.

Dashboard Provisioning (dashboard.yml)

apiVersion: 1

providers:
- name: "default"
orgId: 1
folder: ""
type: file
disableDeletion: false
updateIntervalSeconds: 10
options:
path: /var/lib/grafana/dashboards

Datasource Provisioning (datasource.yml)

apiVersion: 1

datasources:
- name: postgres-datasource
uid: pgds #THIS IS IMPORTANT ON THE DASHBOARD MUST BE THE SAME
orgId: 1
type: postgres
access: proxy
url: pg-db:5432
database: testdb
user: postgres
basicAuth: false
secureJsonData:
password: "postgres"
jsonData:
sslmode: "disable"
tlsAuth: false
tlsAuthWithCACert: false
connMaxLifetime: 14400
postgresVersion: 903
timescaledb: false
isDefault: true

Now we will provides the dashboards files

  1. Create the dashboards folder at the same level of the provisioning folder
  2. Copy your dashboards
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 1,
"links": [],
"liveNow": false,
"panels": [
{
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"axisSoftMin": 0,
"fillOpacity": 80,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineWidth": 1
},
"mappings": [],
"thresholds": {
"mode": "percentage",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "orange",
"value": 70
},
{
"color": "red",
"value": 85
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 0
},
"id": 2,
"options": {
"barWidth": 0.97,
"groupWidth": 0.7,
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"orientation": "auto",
"showValue": "auto",
"stacking": "none",
"tooltip": {
"mode": "single"
},
"xTickLabelRotation": 0
},
"pluginVersion": "8.3.7",
"targets": [
{
"datasource": {
"type": "postgres",
"uid": "pgds"
},
"format": "table",
"group": [
{
"params": ["color"],
"type": "column"
}
],
"hide": false,
"metricColumn": "color",
"rawQuery": true,
"rawSql": "SELECT\n \n color AS metric,\n count(*) AS \"quantity\"\nFROM cars\nWHERE\n $__timeFilter(registration_date)\nGROUP BY color\nORDER BY 1\n",
"refId": "A",
"select": [
[
{
"params": ["car_id"],
"type": "column"
},
{
"params": ["count"],
"type": "aggregate"
},
{
"params": ["quantity"],
"type": "alias"
}
]
],
"table": "cars",
"timeColumn": "registration_date",
"timeColumnType": "date",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Panel Title",
"type": "barchart"
}
],
"refresh": false,
"schemaVersion": 34,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-5y",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Color Cars",
"uid": "XZDyKc64k",
"version": 2,
"weekStart": ""
}

Finally we create the Docker-compose file to put all together to work.

version: "3"
services:
grafana:
build:
context: grafana
dockerfile: Dockerfile
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana

depends_on:
- pg-db

pg-db:
build:
context: postgres
dockerfile: Dockerfile
ports:
- "1599:5432"
volumes:
- postgres-data:/var/lib/postgresql/data

volumes:
grafana-data:
postgres-data:

Conclusion:

Grafana is a great tool, I try show a easy way to deploy it quickly.

I hope that this will useful for you. Thanks for reading.
Here is the Repo

--

--

Sebastian Torralba
AvengaLATAM

Software Architect / Java Team Leader at IncluIT Professor at UNLaR