Skip to content

Commit e4e31f1

Browse files
authored
Add Antigravity Rules, implement them in existing config (#8)
* feat: add repository rules and update README * fix(monitoring): pin images, isolate dbs
1 parent 9c67d15 commit e4e31f1

6 files changed

Lines changed: 116 additions & 42 deletions

File tree

.antigravity/rules.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Repository Rules: RaspberryPi
2+
3+
This file defines the standards and requirements for the `geeksbsmrt/RaspberryPi` home lab repository.
4+
5+
## 1. Technical Stack
6+
7+
- **Primary Scripting**: Bash (.sh) is the preferred language for orchestration and local maintenance.
8+
- **Orchestration**: Docker Compose for service management.
9+
- **Excluded**: PowerShell 7 and .NET/C# are discouraged for this repository unless explicitly requested.
10+
11+
## 2. Networking & IP Management
12+
13+
The environment primarily uses the `192.168.254.0/24` range for Docker containers within the broader `192.168.0.0/16` home network.
14+
15+
### Allocation Strategy
16+
17+
- **Public Services**: Internet-routable through Caddy. Assigned from the **bottom** of the macvlan range (e.g., `.1`, `.2`, `.3`...).
18+
- **Internal Services**: Databases, support services (e.g., Unbound). Assigned from the **top** of the macvlan range (e.g., `.254`, `.253`, `.252`...).
19+
20+
### Network Isolation
21+
22+
- **Rule**: Backend services (Databases, Redis, etc.) should use internal Docker bridge networks where possible. Only front-facing services (Caddy, Pi-hole) or those requiring direct subnet access should be exposed to the `macvlan` network.
23+
24+
### Naming Conventions
25+
26+
- **Variable**: `IP_SERVICE(_FUNCTION)` (e.g., `IP_PIHOLE`, `IP_UMAMI_DB`).
27+
- **Container**: Must be named for the service they provide.
28+
29+
## 3. Secret Management
30+
31+
- **Primary Source**: `secrets.sops.env` (Encrypted via SOPS/AGE).
32+
- **Secondary**: `docker/.env` (Local plaintext, derived from or kept in sync with SOPS).
33+
- **Requirement**: The following MUST be placed in both files and encrypted in the `.sops.env`:
34+
- API keys, passwords, and tokens.
35+
- **Internal IP addresses** (any IP within the `192.168.0.0/16` range).
36+
- **Rule**: Never commit `docker/.env` directly to Git.
37+
38+
## 4. Security & Industry Standards
39+
40+
To ensure the home lab remains secure and stable, the following standards apply:
41+
42+
### Container Security
43+
44+
- **Image Pinning**: Avoid `:latest` or `:alpine` tags without a version number. All images must be pinned to a specific version (e.g., `image: postgres:16.1-alpine`).
45+
- **Least-Privilege**: Containers should run as non-root users (`user: "1000:1000"`) where compatible.
46+
- **Healthchecks**: Every service in `docker-compose.yml` must include a functional `healthcheck`.
47+
- **Logging Configuration**: Limit log sizes to prevent disk exhaustion (e.g., `max-size: "10m"`, `max-file: "3"`).
48+
49+
### Data Persistence
50+
51+
- **Standard**: Persistent data volumes should be mapped to a standard path (e.g., `./data/service_name`) or clearly organized within the service directory to simplify backups.
52+
53+
### Quality Control
54+
55+
- **Linting**: All `docker-compose.yml` and `sh` files must pass `hadolint` and `shellcheck` via pre-commit hooks.
56+
- **Secret Scanning**: Pre-commit hooks must be active to prevent plaintext leakages.
57+
58+
## 5. Operational Workflow
59+
60+
- **Git-First Deployment**: All changes MUST be committed to the repository. Deployment is handled by GitHub Actions (`deploy-prod.yaml`).
61+
- **Emergency Exception (Hotfix)**:
62+
- In a "Service Down" situation, manual fixes may be applied to the live instance **ONLY after explicit USER permission**.
63+
- Once verified, changes **MUST** be immediately committed to Git.
64+
- **Idempotency**: All setup scripts must be safe to run multiple times.
65+
- **Documentation**: Maintain `ReadMe.md` parity with cluster changes.

ReadMe.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
This repository contains configuration files and resources for setting up and managing a Raspberry Pi-based home lab environment. It leverages tools like Docker, pre-commit hooks, and encrypted secrets management to ensure a secure and maintainable setup.
66

77
## Features
8+
89
- Dockerized Services: Containerized applications for easy deployment and scalability.
910
- Pre-commit Hooks: Automated code quality checks to maintain code standards.
1011
- Encrypted Secrets Management: Secure handling of sensitive information using SOPS.
1112
- CI/CD Workflows: Automated workflows for testing and deployment.
1213

1314
## Repository Structure
15+
1416
```plaintext
1517
.github/workflows/ # GitHub Actions workflows for CI/CD
1618
.gitignore # Specifies files to ignore in Git

docker/docker-compose.yml

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,33 @@
11
services:
22
cloudflare_ddns:
3-
image: favonia/cloudflare-ddns:latest # This is the official image, it supports ARM64
3+
image: favonia/cloudflare-ddns:1.15.0
44
container_name: cloudflare_ddns
55
hostname: cloudflare_ddns
66
restart: unless-stopped
77
environment:
8-
# --- Cloudflare Credentials ---
98
- CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}
10-
11-
# --- Domains to Update ---
129
- DOMAINS=${CF_DDNS_DOMAINS}
13-
14-
# --- Other Settings ---
1510
- PROXIED=${CF_DDNS_PROXIED}
1611
- UPDATE_CRON=@every 5m
1712
- IP6_PROVIDER=none
1813
- TZ="America/New_York"
1914

2015
pihole:
2116
container_name: pihole
22-
image: pihole/pihole:latest
17+
image: pihole/pihole:2026.02.0
2318
hostname: pihole
2419
networks:
2520
macvlan:
2621
ipv4_address: ${IP_PIHOLE}
27-
pihole_bridge: {}
22+
backend_net: {}
2823
ports:
2924
- "53:53/tcp"
3025
- "53:53/udp"
3126
environment:
3227
TZ: "America/New_York"
3328
FTLCONF_webserver_api_password: ${PIHOLE_UI_PASSWORD}
3429
FTLCONF_dns_listeningMode: "all"
35-
FTLCONF_dns_upstreams: "${IP_UNBOUND}" # Uses Unbound's parameterized IP
30+
FTLCONF_dns_upstreams: "${IP_UNBOUND}"
3631
FTLCONF_dhcp_active: "true"
3732
FTLCONF_dhcp_start: "${PIHOLE_DHCP_START}"
3833
FTLCONF_dhcp_end: "${PIHOLE_DHCP_END}"
@@ -54,7 +49,7 @@ services:
5449

5550
unbound:
5651
container_name: unbound
57-
image: "mvance/unbound-rpi:latest"
52+
image: "mvance/unbound-rpi:1.22"
5853
hostname: unbound
5954
command: >
6055
sh -c "
@@ -84,7 +79,7 @@ services:
8479
caddy:
8580
container_name: caddy
8681
hostname: caddy
87-
image: ghcr.io/caddybuilds/caddy-cloudflare:latest
82+
image: ghcr.io/caddybuilds/caddy-cloudflare:2.10.2
8883
restart: unless-stopped
8984
logging:
9085
driver: "json-file"
@@ -94,6 +89,7 @@ services:
9489
networks:
9590
macvlan:
9691
ipv4_address: ${IP_CADDY}
92+
backend_net: {}
9793
volumes:
9894
- ./caddy/Caddyfile:/etc/caddy/Caddyfile
9995
- ./caddy/error-pages:/opt/caddy-error-pages:ro
@@ -122,13 +118,12 @@ services:
122118
start_period: 30s
123119

124120
umami_db:
125-
image: postgres:alpine
121+
image: postgres:17-alpine
126122
container_name: umami_db
127123
hostname: umami_db
128124
restart: unless-stopped
129125
networks:
130-
macvlan:
131-
ipv4_address: ${IP_UMAMI_DB}
126+
backend_net: {}
132127
volumes:
133128
- umami_db_data:/var/lib/postgresql/data
134129
environment:
@@ -142,18 +137,19 @@ services:
142137
retries: 5
143138

144139
umami_app:
145-
image: docker.umami.is/umami-software/umami:postgresql-latest
140+
image: ghcr.io/umami-software/umami:postgresql-3.0.3
146141
container_name: umami_app
147142
hostname: umami_app
148143
restart: unless-stopped
149144
networks:
150145
macvlan:
151146
ipv4_address: ${IP_UMAMI_APP}
147+
backend_net: {}
152148
depends_on:
153149
umami_db:
154150
condition: service_healthy
155151
environment:
156-
DATABASE_URL: postgresql://${UMAMI_DB_USER}:${UMAMI_DB_PASSWORD}@${IP_UMAMI_DB}:5432/${UMAMI_DB_NAME} # Uses Umami DB's parameterized IP
152+
DATABASE_URL: postgresql://${UMAMI_DB_USER}:${UMAMI_DB_PASSWORD}@umami_db:5432/${UMAMI_DB_NAME}
157153
DATABASE_TYPE: postgresql
158154
APP_SECRET: ${UMAMI_APP_SECRET}
159155
IGNORE_IP: ${UMAMI_IGNORE_IP}
@@ -169,9 +165,8 @@ services:
169165
retries: 3
170166
start_period: 30s
171167

172-
# --- Monitoring Services ---
173168
prometheus:
174-
image: prom/prometheus:latest
169+
image: prom/prometheus:v3.9.1
175170
container_name: prometheus
176171
hostname: prometheus
177172
restart: unless-stopped
@@ -186,6 +181,7 @@ services:
186181
networks:
187182
macvlan:
188183
ipv4_address: ${IP_PROMETHEUS}
184+
backend_net: {}
189185
healthcheck:
190186
test:
191187
[
@@ -198,7 +194,7 @@ services:
198194
start_period: 30s
199195

200196
grafana:
201-
image: grafana/grafana-oss:latest
197+
image: grafana/grafana-oss:12.1.0
202198
container_name: grafana
203199
hostname: grafana
204200
restart: unless-stopped
@@ -213,6 +209,7 @@ services:
213209
networks:
214210
macvlan:
215211
ipv4_address: ${IP_GRAFANA}
212+
backend_net: {}
216213
healthcheck:
217214
test:
218215
[
@@ -225,7 +222,7 @@ services:
225222
start_period: 30s
226223

227224
rpi_node_exporter:
228-
image: prom/node-exporter:latest
225+
image: prom/node-exporter:v1.8.2
229226
container_name: rpi_node_exporter
230227
hostname: rpi_node_exporter
231228
restart: unless-stopped
@@ -252,11 +249,10 @@ services:
252249
start_period: 30s
253250

254251
cadvisor:
255-
image: gcr.io/cadvisor/cadvisor:latest
252+
image: gcr.io/cadvisor/cadvisor:v0.56.2
256253
container_name: cadvisor
257254
hostname: cadvisor
258255
restart: unless-stopped
259-
# privileged: true
260256
devices:
261257
- /dev/kmsg:/dev/kmsg
262258
volumes:
@@ -267,9 +263,10 @@ services:
267263
networks:
268264
macvlan:
269265
ipv4_address: ${IP_CADVISOR}
266+
backend_net: {}
270267

271268
blackbox_exporter:
272-
image: prom/blackbox-exporter:latest
269+
image: prom/blackbox-exporter:v0.28.0
273270
container_name: blackbox
274271
hostname: blackbox
275272
restart: unless-stopped
@@ -280,6 +277,7 @@ services:
280277
networks:
281278
macvlan:
282279
ipv4_address: ${IP_BLACKBOX}
280+
backend_net: {}
283281
healthcheck:
284282
test:
285283
[
@@ -292,7 +290,7 @@ services:
292290
start_period: 30s
293291

294292
uptime_kuma:
295-
image: louislam/uptime-kuma:latest
293+
image: louislam/uptime-kuma:2.1.0
296294
container_name: uptime_kuma
297295
hostname: uptime_kuma
298296
restart: unless-stopped
@@ -304,6 +302,7 @@ services:
304302
networks:
305303
macvlan:
306304
ipv4_address: ${IP_UPTIME_KUMA}
305+
backend_net: {}
307306

308307
volumes:
309308
caddy_data:
@@ -314,7 +313,7 @@ volumes:
314313
umami_db_data:
315314

316315
networks:
317-
pihole_bridge:
316+
backend_net:
318317
driver: bridge
319318
macvlan:
320319
name: pi0vlan
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: 1
2+
3+
datasources:
4+
- name: Prometheus
5+
type: prometheus
6+
access: proxy
7+
url: http://prometheus:9090
8+
isDefault: true
9+
editable: true
Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
# ./docker/prometheus/config/prometheus.yml.template
1+
# ./docker/data/prometheus/config/prometheus.yml.template
22
global:
33
scrape_interval: 30s
44
evaluation_interval: 30s
55

66
scrape_configs:
7-
- job_name: 'prometheus_self' # Renamed for clarity from 'self'
7+
- job_name: 'prometheus_self'
88
static_configs:
9-
- targets: ['${IP_PROMETHEUS}:9090']
9+
- targets: ['prometheus:9090']
1010

1111
- job_name: 'rpi_node_exporter'
1212
static_configs:
13-
- targets: ['${IP_PI_HOST}:9100'] # Using variable for Pi Host IP
13+
- targets: ['${IP_PI_HOST}:9100']
1414

1515
- job_name: 'docker_cadvisor'
1616
static_configs:
17-
- targets: ['${IP_CADVISOR}:8080']
17+
- targets: ['cadvisor:8080']
1818

19-
- job_name: 'caddy_metrics' # Renamed for clarity from 'caddy'
19+
- job_name: 'caddy_metrics'
2020
static_configs:
21-
- targets: ['${IP_CADDY}:2019']
21+
- targets: ['caddy:2019']
2222
metrics_path: /metrics
2323

2424
- job_name: 'website_and_http_checks'
@@ -30,27 +30,27 @@ scrape_configs:
3030
- https://geeksbsmrt.com
3131
- https://smrtgeekdevs.com
3232
- https://pihole.smrtgeekdevs.com
33-
- http://${IP_PIHOLE}/admin/ # Using variable for Pi-hole IP
33+
- http://pihole/admin/
3434
relabel_configs:
3535
- source_labels: [__address__]
3636
target_label: __param_target
3737
- source_labels: [__param_target]
3838
target_label: instance
3939
- target_label: __address__
40-
replacement: ${IP_BLACKBOX}:9115 # Blackbox Exporter's MacVlan IP
40+
replacement: blackbox:9115
4141

4242
- job_name: 'dns_service_checks'
4343
metrics_path: /probe
4444
params:
45-
module: [dns_probe] # From your blackbox.yml
45+
module: [dns_probe]
4646
static_configs:
4747
- targets:
48-
- '${IP_PIHOLE}:53' # Pi-hole DNS using variable
49-
- '${IP_UNBOUND}:53' # Unbound DNS using variable
48+
- 'pihole:53'
49+
- 'unbound:53'
5050
relabel_configs:
5151
- source_labels: [__address__]
5252
target_label: __param_target
5353
- source_labels: [__param_target]
5454
target_label: instance
5555
- target_label: __address__
56-
replacement: ${IP_BLACKBOX}:9115 # Blackbox Exporter's MacVlan IP
56+
replacement: blackbox:9115

secrets.sops.env

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ UMAMI_DB_PASSWORD=ENC[AES256_GCM,data:4bQhEPIXYL2TTZ44uv08E9jtLffXEIpnZnPLRFfeRN
2121
UMAMI_DB_NAME=ENC[AES256_GCM,data:1+2NS6UT6A==,iv:kAVNAT1jcHU00t2MyziklXjAUfWf+Ln/Whj1JFmFwog=,tag:fbKzHkzJ8vrmvP0pe/4PTQ==,type:str]
2222
UMAMI_APP_SECRET=ENC[AES256_GCM,data:eXzliwppSd9PIXCTiPO4duEiG7RGuIdJ4jq1QvaQpVmRrcJWxtp46f6kMU7T2oIUMqm1yqfDwG1mx6hha8ShVKoW,iv:6+tpBYvBUym7dNaGoi+zLVgHpDTKEzC2FgTZVoK0/bQ=,tag:4Oqm/1/PiYEyVc3LFvYYDw==,type:str]
2323
UMAMI_IGNORE_IP=ENC[AES256_GCM,data:p3EkeLHf5I/R3Poy8nqfxJ6YryXEufAyi4MwV27P,iv:u/CWZQElilijNSi+YnH+Ldo67cTjKaDqD6UlpEHWynI=,tag:x63EZ9KtxH3crTHAi+aiMQ==,type:str]
24-
#ENC[AES256_GCM,data:O4ErD6Yu6ciYYcQ=,iv:GyzrQXolGb8PPxOVRPePXpuacXvzILqdo3lLSt6ZL9s=,tag:Rc5o9vaLIwQbMD78LjeMyQ==,type:comment]
24+
#ENC[AES256_GCM,data:BwT7te5gQbmFgXI=,iv:jJ+B6i+bRY9+bRJUxBb3I0NqFgb+98lnFcBHnEkfOyw=,tag:0yz0oRz6SAEF6oiwrhj8CA==,type:comment]
2525
#ENC[AES256_GCM,data:/zJ6SW4mtPWwZMaqb53S0N9+O+TOP90=,iv:fN+ZjgvMtUZVieejn51qN9Cav0mSrMU1yzlrRpZaO9I=,tag:Gq2YNpFH/1v5Pkvp9HrETA==,type:comment]
2626
MACVLAN_PARENT_INTERFACE=ENC[AES256_GCM,data:Oc0mSc4P,iv:BF7SM4BHwaVaGtPnJQwrWtTbNszKd+hlCXTXVFjfoGo=,tag:QZuECgcnq4CwCLCxnGNSdA==,type:str]
2727
MACVLAN_SUBNET=ENC[AES256_GCM,data:wQiY9j+F+oOcUO3St7PijQ==,iv:b6pgj5RWmfPHngcuremdxgew6Zl4UL1iVVa3NCSlwu0=,tag:IUOb+5FHLwkrTuEwpjc/iA==,type:str]
@@ -33,7 +33,6 @@ IP_PI_HOST=ENC[AES256_GCM,data:bxr2Pvm4fCj6tBJyMA==,iv:q7eso5ZTooNM6nbAsOR1fi0q8
3333
IP_PIHOLE=ENC[AES256_GCM,data:FSa60PLQOZdyc41CsIWr,iv:UcDZPGLLfJqXm/FJzETRKM0U8lCE0lm9qQvyh8Cm+wc=,tag:nqx2ZImDgePjy6PsuzKDqg==,type:str]
3434
IP_UNBOUND=ENC[AES256_GCM,data:cwz2e1melJmtFMQ3O98/P/8=,iv:3tIeSw0WYAKQG6MuoXw9/U/xBkXLSpjkOUjEu6TV6Ts=,tag:EwMw4H9o1UT8xPbI0T5wYA==,type:str]
3535
IP_CADDY=ENC[AES256_GCM,data:hn0w+klRJE1ER6RfuoKd,iv:+d3Bv5I8ULfrofT+47Mh/TPVR+GOoCH6OH5GXTLQtEk=,tag:HGz97ePJw41RyHADe7swtg==,type:str]
36-
IP_UMAMI_DB=ENC[AES256_GCM,data:uWVRMxJFTZze1dqYfXHb,iv:HHbkWb424OvMK4+TNP6SqnX6Kg9vEk62IrE+FS4ILuU=,tag:/32CShU1QEuZUSNNCynK3Q==,type:str]
3736
IP_UMAMI_APP=ENC[AES256_GCM,data:nEY5/dFflGdseQnO39zd,iv:nIwXDisLj2ok1PQTm7S7sXeUF3mpMptOFYWWTelijXc=,tag:bTFIZ5C/kLFh9ZwOsHxZnQ==,type:str]
3837
IP_PROMETHEUS=ENC[AES256_GCM,data:fXuw5TtzH2jWWzD/SnvQlYo=,iv:CLn2JAtWcOnJUsigPWDswnzxcLcfXub/amnuqhHXhwY=,tag:hSp+vaEhrbtXQ1QQWRNFiw==,type:str]
3938
IP_GRAFANA=ENC[AES256_GCM,data:W3VPCahG9n3/i9320FY+0pU=,iv:LugJLyNrcuPvVD8GwSYvLQt89El4KVglCmCwW/HA4kM=,tag:hBElnJdU4NPvSFHJNDb4wg==,type:str]
@@ -44,7 +43,7 @@ sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb2
4443
sops_age__list_0__map_recipient=age13pmf2jna228g9n780j7tl63qdws4gdll36rtuvk74nld5pet543q5ch3wf
4544
sops_age__list_1__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3VDZzb0IvcXAvcWpRY2pK\nL0txc3ZNVFhSN0NJVCswL05LWHhudHl2eGdZCm9JLy9BNlJrdFVsNlM4WmxNNXdY\nVW1pK3AxOVJvTW1SRWNRUTVUT1BseUEKLS0tIGhXTDE3eG9ucnVuY3dsTW1tbUZq\nTWszMjNwVFA1dUxTaTNUbWRvTkJmWUEKr+qniEgTJ5mBQ0wHGxlMQnj3zNWBdkHZ\nlbpdEQbWsSKAdtPvnKvW//A4gUueemGrTHTBkpiAR8svW5JVlpEImw==\n-----END AGE ENCRYPTED FILE-----\n
4645
sops_age__list_1__map_recipient=age1ryc8jh6xye29kgaxqwy57n7qunxak5w8kdtpgk0lwwjuh3w2gywsha6ja2
47-
sops_lastmodified=2026-02-19T03:21:57Z
48-
sops_mac=ENC[AES256_GCM,data:ep+4OHLV1CH9weZGlg6f4si+woDIC0N46IFKenDADWweaky+A0ijJMWGGkAtrSRysZkDMDyThCBGcxmrct0V0jtFBMq77Ug1vHAA5N1g+eGkoFClsQe2cy0RgQ+Srq5FfRu67Mx9LS+XYUC4fMqqirxIqiLDkLW05z+Vz69Br94=,iv:RO4knUgDq+JOfjNjsPpFEdsTnVUziXbLmoO0UTrvWbw=,tag:Ye7D68K9YW0bXjYqN17Jgw==,type:str]
46+
sops_lastmodified=2026-02-21T02:20:36Z
47+
sops_mac=ENC[AES256_GCM,data:FokRAhjVCcMmuysMQ9Va5/IN1JAai1Uj9wyYyxfI4PAkRRg3o+bjKy6sCRpAUzSbJGfYtlaZEzimFBNeZMDo8bUlAv7Me9OyieixaUkre3HzCZH6v2cwBziJTYMnVkLS157okzZh+UcehLnmPnPkzUIdmCoLND8a36rTwaTDcO4=,iv:QZFgQqy8GFQVbnXHXsElvOUEMX0PDtif9db9s41ocds=,tag:GgKix8hG/KQVbRRZnyOx1g==,type:str]
4948
sops_unencrypted_suffix=_unencrypted
5049
sops_version=3.11.0

0 commit comments

Comments
 (0)