Compare commits
11 Commits
98d437e58b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
168198e8a7 | ||
|
|
c3b3b82614 | ||
|
|
7d83de53c5 | ||
|
|
b458c02cf7 | ||
| 4ee7f5b5a1 | |||
|
|
fa36078fbe | ||
|
|
e94e45b3c6 | ||
| 992540360c | |||
| 10c20febca | |||
| 727114220b | |||
| 0cec23b7d9 |
22
.devcontainer/devcontainer.json
Normal file
22
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||||
|
// README at: https://github.com/devcontainers/templates/tree/main/src/alpine
|
||||||
|
{
|
||||||
|
"name": "Debian",
|
||||||
|
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||||
|
"image": "mcr.microsoft.com/devcontainers/base:debian-12"
|
||||||
|
|
||||||
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
|
// "features": {},
|
||||||
|
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
// "forwardPorts": [],
|
||||||
|
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
// "postCreateCommand": "uname -a",
|
||||||
|
|
||||||
|
// Configure tool-specific properties.
|
||||||
|
// "customizations": {},
|
||||||
|
|
||||||
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
|
// "remoteUser": "root"
|
||||||
|
}
|
||||||
9
.gitea/workflows/test.yaml
Normal file
9
.gitea/workflows/test.yaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
name: Gitea Actions Demo
|
||||||
|
run-name: ${{ gitea.actor }} is testing Gitea Actions 🚀
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
Explore-Gitea-Actions:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- run: echo "🎉 The job was automatically triggered by a ${{ gitea.event_name }} event."
|
||||||
|
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by Gitea!"
|
||||||
@@ -7,7 +7,7 @@ Goal: Move from "reactive" to "proactive" management.
|
|||||||
|
|
||||||
## 🤖 Phase 2: CI/CD & Automation
|
## 🤖 Phase 2: CI/CD & Automation
|
||||||
Goal: Fully automate the "Update Dash" and backup workflows.
|
Goal: Fully automate the "Update Dash" and backup workflows.
|
||||||
* **Gitea Actions:** Set up a local Gitea Runner.
|
* **Gitea Actions:** Deployment of local `gitea-act-runner` completed (see manifest).
|
||||||
* **Workflow Integration:** Replace the manual `updatedash` script with an Action that triggers a Docker build and deploy automatically upon every `git push` to the dashboard repo.
|
* **Workflow Integration:** Replace the manual `updatedash` script with an Action that triggers a Docker build and deploy automatically upon every `git push` to the dashboard repo.
|
||||||
|
|
||||||
## 📧 Phase 3: Application Optimization
|
## 📧 Phase 3: Application Optimization
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
| App Name | Subdomain | Internal Port | DB Name |
|
|
||||||
| :.... | :.... | :.... | :.... |
|
|
||||||
| Gitea | git | 3000 | gitea |
|
|
||||||
| Linkwarden | links | 3000 | linkwarden |
|
|
||||||
| FreshRSS | news | 80 | freshrss |
|
|
||||||
| Memos | memos | 5230 | memos |
|
|
||||||
| Dashboard | home | 80 | None |
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
The "Linode App Deployment" SOP:
|
|
||||||
Database Provisioning:
|
|
||||||
|
|
||||||
Update global-db/compose.yaml (for future-proofing).
|
|
||||||
|
|
||||||
Run manual SQL to create DB/User/Owner (since it's a live instance).
|
|
||||||
|
|
||||||
Container Setup:
|
|
||||||
|
|
||||||
Create /opt/docker/app_name.
|
|
||||||
|
|
||||||
Draft compose.yaml using the web_gateway and db_network.
|
|
||||||
|
|
||||||
Run docker compose up -d.
|
|
||||||
|
|
||||||
Networking & Security:
|
|
||||||
|
|
||||||
Add Public Hostname in Cloudflare Zero Trust.
|
|
||||||
|
|
||||||
(Optional) Add Cloudflare Access Policy for MFA.
|
|
||||||
|
|
||||||
Integration:
|
|
||||||
|
|
||||||
Add Link to the Antigravity Dashboard via local PC.
|
|
||||||
|
|
||||||
git push -> updatedash on Linode.
|
|
||||||
23
README.md
23
README.md
@@ -1,2 +1,23 @@
|
|||||||
# homelab-docs
|
# 🛡️ My Cloud Hub: Homelab Documentation
|
||||||
|
|
||||||
|
Welcome to the documentation for my Linode-based homelab. This repository serves as the source of truth for architecture, inventory, and standard operating procedures.
|
||||||
|
|
||||||
|
## 🗺️ Navigation
|
||||||
|
|
||||||
|
### [🏗️ Infrastructure Overview](infrastructure.md)
|
||||||
|
Hardware specs, network topology, and security model.
|
||||||
|
|
||||||
|
### [📦 Service Inventory](inventory.md)
|
||||||
|
Active applications, subdomains, and database mappings.
|
||||||
|
|
||||||
|
### [🛠️ Operations (SOPs)](operations/)
|
||||||
|
- [Deploying a New App](operations/new-app-deployment.md)
|
||||||
|
- [SSH & Key Management](operations/ssh-management.md)
|
||||||
|
- [Dashboard Updates](operations/dashboard-updates.md)
|
||||||
|
- [Disaster Recovery](operations/disaster-recovery.md)
|
||||||
|
|
||||||
|
### [⚡ Daily Cheat Sheet](cheat-sheet.md)
|
||||||
|
Quick commands for monitoring and maintenance.
|
||||||
|
|
||||||
|
### [🚀 2026 Roadmap](2026-roadmap.md)
|
||||||
|
Future projects and infrastructure goals.
|
||||||
|
|||||||
28
apps/gitea/compose.yaml
Normal file
28
apps/gitea/compose.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
services:
|
||||||
|
server:
|
||||||
|
image: gitea/gitea:latest
|
||||||
|
container_name: gitea
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
- GITEA__database__DB_TYPE=postgres
|
||||||
|
- GITEA__database__HOST=global_postgres:5432 # Point to the universal container
|
||||||
|
- GITEA__database__NAME=gitea
|
||||||
|
- GITEA__database__USER=gitea
|
||||||
|
- GITEA__database__PASSWD=check-password-in-keeper
|
||||||
|
# SSH configuration
|
||||||
|
- GITEA__server__START_SSH_SERVER=true
|
||||||
|
- GITEA__server__SSH_DOMAIN=git-ssh.davisdre.com
|
||||||
|
- GITEA__server__SSH_PORT=22
|
||||||
|
- GITEA__server__SSH_LISTEN_PORT=2222
|
||||||
|
- GITEA__server__ROOT_URL=https://git.davisdre.com/
|
||||||
|
- GITEA__repository__ENABLE_PUSH_CREATE_USER=true
|
||||||
|
- GITEA__repository__ENABLE_PUSH_CREATE_ORG=true
|
||||||
|
networks:
|
||||||
|
- db_network
|
||||||
|
- web_gateway
|
||||||
|
|
||||||
|
networks:
|
||||||
|
db_network:
|
||||||
|
external: true
|
||||||
|
web_gateway:
|
||||||
|
external: true
|
||||||
102
cheat-sheet.md
102
cheat-sheet.md
@@ -1,71 +1,53 @@
|
|||||||
# ⚡ My Cloud Hub: Daily Cheat Sheet
|
# ⚡ Daily Cheat Sheet
|
||||||
|
|
||||||
## 🚀 Dashboard Updates
|
Quick commands for common homelab tasks. For detailed procedures, see the [Operations](./operations/) directory.
|
||||||
Run this after pushing changes from your local PC to Gitea.
|
|
||||||
|
|
||||||
# Using the alias we created
|
|
||||||
`updatedash`
|
|
||||||
|
|
||||||
# Or manually:
|
|
||||||
`cd /opt/docker/dashboard && ./update-dash.sh`
|
|
||||||
|
|
||||||
## 🐳 Docker Maintenance
|
## 🐳 Docker Maintenance
|
||||||
Essential commands for managing your container stack.
|
```bash
|
||||||
|
# View running containers
|
||||||
|
docker ps
|
||||||
|
|
||||||
# View all running containers and their health
|
# Restart a service
|
||||||
`docker ps`
|
cd /opt/docker/[service] && docker compose restart
|
||||||
|
|
||||||
# Restart a specific service (e.g., memos)
|
# View live logs
|
||||||
`cd /opt/docker/memos && docker compose restart`
|
docker compose logs -f [service]
|
||||||
|
|
||||||
# View live logs for a service to troubleshoot
|
# Cleanup unused data
|
||||||
`docker compose logs -f [service_name]`
|
docker system prune -a --volumes
|
||||||
|
|
||||||
# Clean up unused images/volumes (Run this if disk gets full)
|
|
||||||
`docker system prune -a --volumes`
|
|
||||||
|
|
||||||
## 🐘 Database (Postgres) Operations
|
|
||||||
Quick commands for interacting with your Universal DB.
|
|
||||||
|
|
||||||
# Access the Postgres CLI as the superuser
|
|
||||||
`docker exec -it global_postgres psql -U postgres`
|
|
||||||
```sql
|
|
||||||
# SQL Commands (run these inside psql):
|
|
||||||
# \l -- List all databases
|
|
||||||
# \du -- List all users/roles
|
|
||||||
# SELECT datname, pg_size_pretty(pg_database_size(datname)) FROM pg_database;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 🐘 Database Operations
|
||||||
|
```bash
|
||||||
|
# Access Postgres CLI
|
||||||
|
docker exec -it global_postgres psql -U postgres
|
||||||
|
```
|
||||||
|
*SQL inside psql:* `\l` (list DBs), `\du` (list users).
|
||||||
|
|
||||||
## ☁️ Backup & Cloud Storage
|
## ☁️ Backup & Cloud Storage
|
||||||
Manual triggers and verification for your S3 backups.
|
|
||||||
|
|
||||||
# Run a manual backup immediately
|
|
||||||
`/opt/docker/backups/backup-homelab.sh`
|
|
||||||
|
|
||||||
# List files in your Linode Object Storage bucket
|
|
||||||
`rclone ls linode-s3:davisdre-backups-chicago`
|
|
||||||
|
|
||||||
# Check the backup log for errors
|
|
||||||
`tail -n 20 /opt/docker/backups/backup.log`
|
|
||||||
|
|
||||||
## 🖥️ System Health & Monitoring
|
|
||||||
Keep an eye on that 2GB RAM limit and swap space.
|
|
||||||
|
|
||||||
# The 'Gold Standard' for real-time monitoring
|
|
||||||
`htop`
|
|
||||||
|
|
||||||
# Check disk space usage
|
|
||||||
`df -h`
|
|
||||||
|
|
||||||
# Check swap utilization specifically
|
|
||||||
`swapon --show`
|
|
||||||
|
|
||||||
## 🛠️ Infrastructure SOP: Adding a New App
|
|
||||||
1. Update global-db/compose.yaml environment list.
|
|
||||||
2. Manually provision the DB on the live instance:
|
|
||||||
```bash
|
```bash
|
||||||
docker exec -it global_postgres psql -U postgres -c "CREATE DATABASE app_name;"
|
# Manual backup trigger
|
||||||
docker exec -it global_postgres psql -U postgres -c "CREATE USER app_name WITH PASSWORD 'my-custom-password';"
|
/opt/docker/backups/backup-homelab.sh
|
||||||
docker exec -it global_postgres psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE app_name TO app_name;"
|
|
||||||
docker exec -it global_postgres psql -U postgres -c "ALTER DATABASE app_name OWNER TO app_name;"
|
# List remote backups
|
||||||
|
rclone ls linode-s3:davisdre-backups-chicago
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
tail -n 20 /opt/docker/backups/backup.log
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 🖥️ System Health
|
||||||
|
```bash
|
||||||
|
# Real-time monitoring
|
||||||
|
htop
|
||||||
|
|
||||||
|
# Disk usage
|
||||||
|
df -h
|
||||||
|
|
||||||
|
# Swap space
|
||||||
|
swapon --show
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Quick Actions
|
||||||
|
- **Update Dashboard:** `updatedash` (on Linode)
|
||||||
|
- **New App:** See [New App Deployment](./operations/new-app-deployment.md)
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
# Linode Homelab Configuration Summary (Dec 2025)
|
|
||||||
|
|
||||||
## 1. Hardware & OS
|
|
||||||
- **Provider:** Linode (Akamai) - Chicago (US-ORD)
|
|
||||||
- **Plan:** Shared CPU 2GB RAM / 50GB NVMe
|
|
||||||
- **OS:** Ubuntu 24.04 LTS
|
|
||||||
- **Primary User:** drew (Sudo & Docker groups)
|
|
||||||
- **Host Security:** Disk Encryption enabled (Platform-managed)
|
|
||||||
|
|
||||||
## 2. Storage Architecture
|
|
||||||
- **Volume:** 20GB Block Storage (Encrypted)
|
|
||||||
- **Mount Point:** `/mnt/docker_data`
|
|
||||||
- **Symlink:** `/opt/docker` -> `/mnt/docker_data` (All Docker files live here)
|
|
||||||
- **Mount Logic:** Persistent via `/etc/fstab` with `noatime,nofail`.
|
|
||||||
|
|
||||||
## 3. Docker Networking
|
|
||||||
- **web_gateway:** External bridge network for Cloudflare Tunnel connectivity.
|
|
||||||
- **db_network:** External bridge network for centralized database connectivity.
|
|
||||||
|
|
||||||
## 4. Active Containers
|
|
||||||
- **global_postgres:** PostgreSQL 16 (Universal DB).
|
|
||||||
- Location: `/opt/docker/global-db`
|
|
||||||
- Features: Automatic multi-DB creation via init-script.
|
|
||||||
- **gitea:** Self-hosted Git.
|
|
||||||
- Location: `/opt/docker/gitea`
|
|
||||||
- DB: Universal DB (Postgres)
|
|
||||||
- Access: Via Cloudflare Tunnel (git.yourdomain.com)
|
|
||||||
- **cloudflared-tunnel:** Outbound tunnel to Cloudflare Edge.
|
|
||||||
- Location: `/opt/docker/cloudflared`
|
|
||||||
|
|
||||||
## 5. Security Model
|
|
||||||
- **Inbound:** Restricted to SSH (Port 22).
|
|
||||||
- **Web Access:** No open ports (80/443/3000). All traffic enters via outbound Cloudflare Tunnel.
|
|
||||||
37
infrastructure.md
Normal file
37
infrastructure.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# Infrastructure Overview
|
||||||
|
|
||||||
|
This document describes the physical and logical infrastructure of the Linode Homelab.
|
||||||
|
|
||||||
|
## 🌐 Hardware & OS
|
||||||
|
- **Provider:** Linode (Akamai)
|
||||||
|
- **Location:** Chicago (US-ORD)
|
||||||
|
- **Plan:** Shared CPU 2GB RAM / 50GB NVMe
|
||||||
|
- **OS:** Ubuntu 24.04 LTS (Upgraded from 22.04)
|
||||||
|
- **Primary User:** `drew` (Sudo & Docker groups)
|
||||||
|
- **Security:** Platform-managed Disk Encryption enabled.
|
||||||
|
|
||||||
|
## 💾 Storage Architecture
|
||||||
|
- **Volume:** 20GB Block Storage (Encrypted)
|
||||||
|
- **Mount Point:** `/mnt/docker_data`
|
||||||
|
- **Symlink:** `/opt/docker` -> `/mnt/docker_data`
|
||||||
|
- All Docker-related files (compose files, config, volumes) reside under `/opt/docker`.
|
||||||
|
- **Mount Logic:** Persistent via `/etc/fstab` with `noatime,nofail`.
|
||||||
|
|
||||||
|
## 🖥️ System Tuning
|
||||||
|
- **RAM:** 2GB (Shared)
|
||||||
|
- **Swap:** 1.5GB Total
|
||||||
|
- Partition: `/dev/sdb` (512MB default)
|
||||||
|
- File: `/swapfile` (1GB manual)
|
||||||
|
- **Swappiness:** Default (60)
|
||||||
|
|
||||||
|
## 🏗️ Docker Network Topology
|
||||||
|
| Network | Driver | Purpose |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `web_gateway` | bridge (ext) | External traffic from Cloudflare Tunnels to containers. |
|
||||||
|
| `db_network` | bridge (ext) | Private traffic between Applications and the Global Postgres instance. |
|
||||||
|
|
||||||
|
## 🛡️ Security Model
|
||||||
|
- **Inbound:** Restricted to SSH (Port 22).
|
||||||
|
- **Web Access:** No open ports (80/443/3000). All traffic enters via outbound Cloudflare Tunnels (Zero Trust).
|
||||||
|
- **Authentication:** Protected by Cloudflare Access with MFA/WARP.
|
||||||
|
- **Backups:** Nightly (2:00 AM) to Linode Object Storage (S3) via `rclone`.
|
||||||
17
inventory.md
Normal file
17
inventory.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Service Inventory
|
||||||
|
|
||||||
|
A comprehensive list of all applications and services running in the homelab.
|
||||||
|
|
||||||
|
| Service | Subdomain | Internal Port | Directory | Database | Purpose |
|
||||||
|
| :--- | :--- | :--- | :--- | :--- | :--- |
|
||||||
|
| **Dashboard** | `home` | 80 | `/opt/docker/dashboard` | Static (Nginx) | Homelab landing page. |
|
||||||
|
| **Gitea** | `git` | 22, 3000 | `/opt/docker/gitea` | `gitea` (Postgres) | Self-hosted Git service. |
|
||||||
|
| **Gitea-Act_runner** | none | none | `/opt/docker/gitea` | None | CI/CD Runner for Gitea Actions. |
|
||||||
|
| **Linkwarden** | `links` | 3000 | `/opt/docker/linkwarden` | `linkwarden` (Postgres) | Bookmark and archive manager. |
|
||||||
|
| **FreshRSS** | `news` | 80 | `/opt/docker/freshrss` | `freshrss` (Postgres) | RSS Feed Aggregator. |
|
||||||
|
| **Memos** | `memos` | 5230 | `/opt/docker/memos` | `memos` (Postgres) | Privacy-first note-taking. |
|
||||||
|
| **Surmai** | `travel` | 8080 | `/opt/docker/surmai` | `surmai` (Postgres) | Personal travel itinerary manager. |
|
||||||
|
| **Postgres** | none | 5432 | `/opt/docker/global-db` | **Universal DB** | PostgreSQL 16 centralized database. |
|
||||||
|
| **cloudflared** | none | none | `/opt/docker/cloudflared` | None | Outbound tunnel to Cloudflare Edge. |
|
||||||
|
|
||||||
|
**Note:** All subdomains are under the `davisdre.com` apex domain.
|
||||||
51
manifest.md
51
manifest.md
@@ -1,51 +0,0 @@
|
|||||||
# 🛡️ My Cloud Hub: Architecture & Operations
|
|
||||||
|
|
||||||
## 🌐 Global Architecture
|
|
||||||
* **Compute:** Linode 2GB Shared Instance (Chicago/us-ord)
|
|
||||||
* **OS:** Ubuntu 22.04 LTS
|
|
||||||
* **Ingress:** Cloudflare Zero Trust (Tunnels) with MFA/WARP
|
|
||||||
* **Backups:** Nightly (2:00 AM) to Linode Object Storage (S3) via `rclone`
|
|
||||||
|
|
||||||
## 🖥️ System Specs & Tuning
|
|
||||||
* **RAM:** 2GB (Shared)
|
|
||||||
* **Swap:** 1.5GB Total
|
|
||||||
* Partition: /dev/sdb (512MB default)
|
|
||||||
* File: /swapfile (1GB manual)
|
|
||||||
* **Swappiness:** Default (60)
|
|
||||||
|
|
||||||
## 🏗️ Docker Network Topology
|
|
||||||
| Network | Driver | Purpose |
|
|
||||||
| :--- | :--- | :--- |
|
|
||||||
| `web_gateway` | bridge (ext) | External traffic from Cloudflare to containers |
|
|
||||||
| `db_network` | bridge (ext) | Private traffic between Apps and Postgres |
|
|
||||||
|
|
||||||
## 📦 Service Inventory
|
|
||||||
| Service | URL | Directory | Database |
|
|
||||||
| :--- | :--- | :--- | :--- |
|
|
||||||
| **Dashboard** | `home.davisdre.com` | `/opt/docker/dashboard` | Static (Nginx) |
|
|
||||||
| **Gitea** | `git.davisdre.com` | `/opt/docker/gitea` | `gitea` (Postgres) |
|
|
||||||
| **Linkwarden** | `links.davisdre.com` | `/opt/docker/linkwarden` | `linkwarden` (Postgres) |
|
|
||||||
| **FreshRSS** | `news.davisdre.com` | `/opt/docker/freshrss` | `freshrss` (Postgres) |
|
|
||||||
| **Memos** | `memos.davisdre.com` | `/opt/docker/memos` | `memos` (Postgres) |
|
|
||||||
| **Surmai** | `travel.davisdre.com` | `/opt/docker/surmai` | Internal SQLite |
|
|
||||||
| **Postgres** | *Internal Only* | `/opt/docker/global-db` | **Universal DB** |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛠️ Standard Operating Procedures (SOPs)
|
|
||||||
|
|
||||||
### 1. Updating the Dashboard
|
|
||||||
1. Modify `index.html` on local PC.
|
|
||||||
2. `git add . && git commit -m "update" && git push`
|
|
||||||
3. On Linode, run: `updatedash` (alias for `/opt/docker/dashboard/update-dash.sh`)
|
|
||||||
|
|
||||||
### 2. Adding a New App (Postgres-backed)
|
|
||||||
Run these commands to provision the DB before deploying the container:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Update global-db/compose.yaml environment list first
|
|
||||||
# 2. Manually provision the DB (Live Instance)
|
|
||||||
docker exec -it global_postgres psql -U postgres -c "CREATE DATABASE app_name;"
|
|
||||||
docker exec -it global_postgres psql -U postgres -c "CREATE USER app_name WITH PASSWORD 'my-custom-password';"
|
|
||||||
docker exec -it global_postgres psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE app_name TO app_name;"
|
|
||||||
docker exec -it global_postgres psql -U postgres -c "ALTER DATABASE app_name OWNER TO app_name;"
|
|
||||||
25
operations/dashboard-updates.md
Normal file
25
operations/dashboard-updates.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# SOP: Updating the Dashboard
|
||||||
|
|
||||||
|
The homelab landing page (Antigravity Dashboard) is a static Nginx site managed via Git.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. **Local Development:** Modify `index.html` or assets on your local machine.
|
||||||
|
2. **Push Changes:**
|
||||||
|
```bash
|
||||||
|
git add .
|
||||||
|
git commit -m "Update dashboard: [feature/fix]"
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
3. **Remote Update:**
|
||||||
|
SSH into the Linode instance and run the pre-configured alias:
|
||||||
|
```bash
|
||||||
|
updatedash
|
||||||
|
```
|
||||||
|
*(This is an alias for `/opt/docker/dashboard/update-dash.sh` which performs a `git pull` inside the container's mounted volume).*
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
- If the alias fails, manually navigate to the directory:
|
||||||
|
```bash
|
||||||
|
cd /opt/docker/dashboard && ./update-dash.sh
|
||||||
|
```
|
||||||
@@ -39,4 +39,5 @@ cd /opt/docker/linkwarden && docker compose up -d
|
|||||||
cd /opt/docker/freshrss && docker compose up -d
|
cd /opt/docker/freshrss && docker compose up -d
|
||||||
cd /opt/docker/memos && docker compose up -d
|
cd /opt/docker/memos && docker compose up -d
|
||||||
cd /opt/docker/surmai && docker compose up -d
|
cd /opt/docker/surmai && docker compose up -d
|
||||||
|
cd /opt/docker/cloudflared && docker compose up -d
|
||||||
```
|
```
|
||||||
39
operations/new-app-deployment.md
Normal file
39
operations/new-app-deployment.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# SOP: Deploying a New Application
|
||||||
|
|
||||||
|
This guide outlines the workflow for adding a new service to the homelab.
|
||||||
|
|
||||||
|
## 1. Database Provisioning (Postgres)
|
||||||
|
Most apps use the centralized `global_postgres` instance.
|
||||||
|
|
||||||
|
1. **Update Config:** Add the new database to the environment list in `/opt/docker/global-db/compose.yaml` for documentation.
|
||||||
|
2. **Manual Provisioning:** Run the following commands to create the database and user on the live instance:
|
||||||
|
```bash
|
||||||
|
# Access the Postgres CLI
|
||||||
|
docker exec -it global_postgres psql -U postgres
|
||||||
|
|
||||||
|
# Run these SQL commands:
|
||||||
|
CREATE DATABASE app_name;
|
||||||
|
CREATE USER app_name WITH PASSWORD 'secure-password';
|
||||||
|
GRANT ALL PRIVILEGES ON DATABASE app_name TO app_name;
|
||||||
|
ALTER DATABASE app_name OWNER TO app_name;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Container Setup
|
||||||
|
1. **Directory:** Create a new directory under `/opt/docker/app_name`.
|
||||||
|
2. **Compose File:** Draft a `compose.yaml` file.
|
||||||
|
- Ensure it joins the `web_gateway` (for Cloudflare) and `db_network` (for Postgres).
|
||||||
|
- Use environment variables for DB credentials.
|
||||||
|
3. **Deployment:** Run `docker compose up -d`.
|
||||||
|
|
||||||
|
## 3. Networking & Security
|
||||||
|
1. **Cloudflare Tunnel:**
|
||||||
|
- Log in to Cloudflare Zero Trust Dashboard.
|
||||||
|
- Navigate to **Access > Tunnels**.
|
||||||
|
- Add a **Public Hostname** for the service (e.g., `app.davisdre.com`).
|
||||||
|
2. **Access Policy (Optional):** Add a Cloudflare Access Policy if MFA or WARP is required for this specific app.
|
||||||
|
|
||||||
|
## 4. Integration
|
||||||
|
1. **Dashboard:**
|
||||||
|
- Add the new app link to the Antigravity Dashboard on your local PC.
|
||||||
|
- `git push` the changes.
|
||||||
|
- On Linode, run `updatedash` to reflect changes.
|
||||||
150
operations/ssh-management.md
Normal file
150
operations/ssh-management.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
# SOP: SSH Key Management & Access (Zero Trust)
|
||||||
|
|
||||||
|
**Purpose:** Standardize the creation, storage, and usage of SSH keys for accessing internal homelab services (Gitea, servers, etc.) protected by Cloudflare Tunnels, without opening firewall ports.
|
||||||
|
|
||||||
|
**Prerequisites:**
|
||||||
|
|
||||||
|
* **Client:** Windows 10/11 with OpenSSH Client installed.
|
||||||
|
* **Software:** Keeper Password Manager (Desktop App), `cloudflared` daemon.
|
||||||
|
* **Network:** Cloudflare Tunnel configured for the target service (SSH protocol).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Key Generation
|
||||||
|
|
||||||
|
Use **Ed25519** for all new keys (faster, smaller, more secure than RSA).
|
||||||
|
|
||||||
|
1. Open PowerShell.
|
||||||
|
2. Generate a new key pair (replace `service` with app name, e.g., `gitea`, `prod-server`):
|
||||||
|
```powershell
|
||||||
|
ssh-keygen -t ed25519 -C "davisdre@service" -f "$env:USERPROFILE\.ssh\id_ed25519_service"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
3. **Do not** set a passphrase if relying on Keeper (Keeper protects the key).
|
||||||
|
|
||||||
|
## 2. Storage & Agent Setup (Keeper)
|
||||||
|
|
||||||
|
We do not store private keys permanently on the local disk. They live in Keeper and are injected into memory via the SSH Agent.
|
||||||
|
|
||||||
|
1. **Create Record:** Create a new record in Keeper (e.g., "SSH Key - Gitea").
|
||||||
|
2. **Attach Keys:** Upload the `.pub` (Public) and the private key file (no extension) to the record attachments or dedicated SSH Key fields.
|
||||||
|
3. **Enable Agent:**
|
||||||
|
* In Keeper Desktop: Go to **Settings > SSH Agent**.
|
||||||
|
* Ensure **Enable SSH Agent Integration** is ON.
|
||||||
|
* Select the key record you just created and ensure it is listed/active.
|
||||||
|
|
||||||
|
|
||||||
|
4. **Cleanup:** Delete the **private** key file from your local `.ssh` folder. You may keep the `.pub` file for reference.
|
||||||
|
|
||||||
|
## 3. Client Configuration (`config`)
|
||||||
|
|
||||||
|
Configure the local SSH client to route traffic through Cloudflare and use the Keeper agent.
|
||||||
|
|
||||||
|
1. Open your config file: `C:\Users\davis\.ssh\config`.
|
||||||
|
2. Add a new block for the service.
|
||||||
|
* **Note:** Do *not* hardcode `IdentityAgent` lines; rely on the `SSH_AUTH_SOCK` environment variable set by Keeper.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```text
|
||||||
|
# Template for Cloudflare Tunnel Services
|
||||||
|
Host service.davisdre.com
|
||||||
|
User git
|
||||||
|
# Proxy traffic via Cloudflare (requires cloudflared installed)
|
||||||
|
ProxyCommand cloudflared access ssh --hostname %h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Service Configuration
|
||||||
|
|
||||||
|
1. Copy the content of your **Public Key** (`.pub` file).
|
||||||
|
2. Navigate to the Service (e.g., Gitea Settings > SSH / GPG Keys).
|
||||||
|
3. Add Key and paste the string (starts with `ssh-ed25519`).
|
||||||
|
|
||||||
|
## 5. Connection Verification
|
||||||
|
|
||||||
|
Before using the tool (VS Code, git, etc.), verify the handshake in PowerShell.
|
||||||
|
|
||||||
|
1. **Unlock Keeper:** Ensure the vault is open.
|
||||||
|
2. **Test Connection:**
|
||||||
|
```powershell
|
||||||
|
ssh -T git@service.davisdre.com
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
3. **Expected Output:**
|
||||||
|
* *First time:* Prompts to verify host fingerprint (Type `yes`).
|
||||||
|
* *Success:* `Hi there...! You've successfully authenticated...`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. WSL & Keeper Desktop SSH Agent Setup
|
||||||
|
|
||||||
|
This configuration bridges Windows Subsystem for Linux (WSL) with the Keeper Desktop SSH Agent using native Windows executables.
|
||||||
|
|
||||||
|
### The Core Concept
|
||||||
|
WSL uses Unix sockets for SSH, while Windows applications like Keeper Desktop use Windows Named Pipes. By default, they cannot communicate. The most reliable solution is to instruct WSL to use the native Windows `ssh.exe` executables, which inherently understand Windows Named Pipes and can talk directly to Keeper.
|
||||||
|
|
||||||
|
### Configuration Steps
|
||||||
|
|
||||||
|
1. **Remove Linux SSH-Agent Scripts**
|
||||||
|
If you previously configured your `~/.bashrc` to start the Linux ssh-agent (e.g., `eval "$(ssh-agent -s)"`), remove those lines to prevent conflicts.
|
||||||
|
|
||||||
|
2. **Alias Windows Executables in WSL**
|
||||||
|
Open your bash configuration file:
|
||||||
|
```bash
|
||||||
|
nano ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the following aliases to the bottom of the file:
|
||||||
|
```bash
|
||||||
|
# Use Windows native SSH tools to interface with Keeper Desktop
|
||||||
|
alias ssh='ssh.exe'
|
||||||
|
alias ssh-add='ssh-add.exe'
|
||||||
|
alias scp='scp.exe'
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Configure Git (Optional but Recommended)**
|
||||||
|
If you use Git inside WSL and want to push/pull using Keeper's SSH keys, configure Git to use the Windows SSH executable:
|
||||||
|
```bash
|
||||||
|
git config --global core.sshCommand "ssh.exe"
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Apply the Changes**
|
||||||
|
Reload your shell configuration:
|
||||||
|
```bash
|
||||||
|
source ~/.bashrc
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
To confirm the setup is working, run:
|
||||||
|
```bash
|
||||||
|
ssh-add -l
|
||||||
|
```
|
||||||
|
*(This executes `ssh-add.exe -l` under the hood via the alias).*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Issue | Check |
|
||||||
|
| --- | --- |
|
||||||
|
| **Permission Denied (publickey)** | 1. Is Keeper unlocked? <br> <br> 2. Run `ssh-add -l` to see if keys are loaded. <br> <br> 3. Ensure `git config core.sshCommand` is set to Windows OpenSSH. |
|
||||||
|
| **"The agent has no identities"** | WSL can see Keeper, but Keeper isn't providing keys. Check that the vault is unlocked and the specific key record has "Add to SSH Agent" toggled ON. |
|
||||||
|
| **"Error connecting to agent"** | The Keeper SSH Agent is not running. In Keeper Desktop Settings > Developer, toggle the SSH Agent OFF and back ON. |
|
||||||
|
| **TLS Handshake Failure** | Cloudflare SSL mismatch. Ensure the tunnel hostname is not 4th level (e.g., use `git-ssh.domain.com`, NOT `ssh.git.domain.com`). |
|
||||||
|
| **"Unknown Port" / Proxy Error** | Ensure `cloudflared` is installed and the Tunnel Public Hostname is set to `SSH` service (not HTTP). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Git Configuration (One-Time Setup)**
|
||||||
|
|
||||||
|
Ensure Git uses the Windows Native SSH (which talks to Keeper) rather than the bundled MinGW SSH.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"
|
||||||
|
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user