homelab infrastructure services
at fix-docker-install 319 lines 10 kB view raw view rendered
1# Service Creation Guide 2 3This guide explains how to create services that work with tinsnip's NFS-backed persistence architecture. 4 5## Overview 6 7tinsnip provides standardized infrastructure for service deployment with: 8- **NFS-backed persistence**: Data survives machine rebuilds 9- **XDG integration**: Data accessible through standard Linux paths 10- **UID isolation**: Each service runs with dedicated user/permissions 11- **Port allocation**: Automatic port assignment based on service UID 12 13Services can be created using two patterns depending on their origin and requirements. 14 15## The tinsnip Target Pattern 16 17tinsnip establishes a standard environment that services can leverage: 18 19### Infrastructure Provided 20 21**NFS Mount Structure:** 22``` 23/mnt/tinsnip/ # NFS mount point 24├── data/ # Persistent application data 25├── config/ # Service configuration files 26├── state/ # Service state (logs, databases, etc.) 27└── service/ # Docker compose location 28 └── myservice/ 29 ├── docker-compose.yml 30 └── setup.sh (optional) 31``` 32 33**Service Environment File (.env):** 34Generated by tinsnip setup with deployment-specific paths: 35```bash 36# Tinsnip deployment - direct NFS mounts 37XDG_DATA_HOME=/mnt/tinsnip/data 38XDG_CONFIG_HOME=/mnt/tinsnip/config 39XDG_STATE_HOME=/mnt/tinsnip/state 40 41# Service metadata 42TIN_SERVICE_NAME=myservice 43TIN_SERVICE_ENVIRONMENT=prod 44TIN_SERVICE_UID=11100 45PRIMARY_PORT=11100 46SECONDARY_PORT=11101 47``` 48 49### Environment Variable Mapping 50 51| Environment Variable | Value (set in .env) | Container Path | 52|---------------------|---------------------|----------------| 53| `TIN_SERVICE_UID` | 11100 | Used for user | 54| `TIN_SERVICE_NAME` | myservice | - | 55| `TIN_SERVICE_ENVIRONMENT` | prod | - | 56| `TIN_NAMESPACE` | dynamicalsystem | - | 57| `PRIMARY_PORT` | 11100 | 11100 | 58| `SECONDARY_PORT` | 11101 | 11101 | 59| `XDG_DATA_HOME` | /mnt/tinsnip/data | /data | 60| `XDG_CONFIG_HOME` | /mnt/tinsnip/config | /config | 61| `XDG_STATE_HOME` | /mnt/tinsnip/state | /state | 62 63### Path Resolution 64 65| Host Path | Container Path | Description | 66|-----------|---------------|-------------| 67| `${XDG_DATA_HOME}/${TIN_NAMESPACE}/${TIN_SERVICE_NAME}` | `/data` | Application data | 68| `${XDG_CONFIG_HOME}/${TIN_NAMESPACE}/${TIN_SERVICE_NAME}` | `/config` | Configuration files | 69| `${XDG_STATE_HOME}/${TIN_NAMESPACE}/${TIN_SERVICE_NAME}` | `/state` | State/logs/cache | 70 71**Example Resolution:** 72```bash 73# For myservice-prod in dynamicalsystem namespace 74${XDG_DATA_HOME}/${TIN_NAMESPACE}/${TIN_SERVICE_NAME} 75(from .env) 76/mnt/tinsnip/data/dynamicalsystem/myservice 77(NFS mount) 78nas-server:/volume1/dynamicalsystem/myservice/prod/data 79``` 80 81## Volume Requirements 82 83**⚠️ CRITICAL**: Services MUST use bind mounts to XDG-integrated NFS directories, not Docker named volumes. 84 85**✅ CORRECT (XDG + NFS-backed persistence):** 86```yaml 87volumes: 88 - ${XDG_DATA_HOME}/${TIN_NAMESPACE}/${TIN_SERVICE_NAME}:/app/data 89 - ${XDG_CONFIG_HOME}/${TIN_NAMESPACE}/${TIN_SERVICE_NAME}:/app/config 90 - ${XDG_STATE_HOME}/${TIN_NAMESPACE}/${TIN_SERVICE_NAME}:/app/state 91``` 92 93**❌ INCORRECT (Local storage - data lost on rebuild):** 94```yaml 95volumes: 96 - myservice_data:/app/data # Stored locally, lost on rebuild 97volumes: 98 myservice_data: # Breaks continuous delivery 99``` 100 101### Why This Matters 102 103**tinsnip's Value Proposition**: Continuous delivery with persistent data that survives machine rebuilds. 104 105- **With XDG Bind Mounts**: Data stored on NFS → Survives machine rebuilds → True continuous delivery 106- **With Named Volumes**: Data stored locally → Lost on rebuild → Breaks continuous delivery 107 108## Pattern 1: Home-grown Services 109 110**Use Case**: Services built specifically for tinsnip that can follow conventions natively. 111 112### Design Principles 113- Built to expect tinsnip's XDG + NFS directory structure 114- Uses environment variables for all configuration 115- Designed for the target UID and port scheme 116- No adaptation layer needed 117 118### Example: Custom Web Service (Gazette) 119 120```yaml 121services: 122 gazette: 123 image: myorg/gazette:latest 124 ports: 125 - "${PRIMARY_PORT}:3000" 126 volumes: 127 - ${XDG_DATA_HOME}/${TIN_NAMESPACE}/${TIN_SERVICE_NAME}:/app/documents 128 - ${XDG_CONFIG_HOME}/${TIN_NAMESPACE}/${TIN_SERVICE_NAME}:/app/config 129 - ${XDG_STATE_HOME}/${TIN_NAMESPACE}/${TIN_SERVICE_NAME}:/app/logs 130 user: "${TIN_SERVICE_UID}:${TIN_SERVICE_UID}" 131 environment: 132 # Service-specific environment variables 133 - GAZETTE_DOCUMENT_ROOT=/app/documents 134 - GAZETTE_CONFIG_FILE=/app/config/gazette.yaml 135 - GAZETTE_LOG_DIR=/app/logs 136 - GAZETTE_PORT=3000 137 - GAZETTE_BASE_URL=http://localhost:${PRIMARY_PORT} 138 - GAZETTE_UID=${TIN_SERVICE_UID} 139 - GAZETTE_NAMESPACE=${TIN_NAMESPACE} 140 restart: unless-stopped 141 networks: 142 - tinsnip_network 143``` 144 145### Home-grown Service Benefits 146- Clean, simple docker-compose.yml 147- No path translations or adaptations needed 148- Full leverage of tinsnip environment 149- Predictable behavior across deployments 150- Direct XDG compliance 151 152## Pattern 2: Third-party Adaptation 153 154**Use Case**: Existing external containers that need to be wrapped to work with tinsnip's conventions. 155 156### Adaptation Strategies 157 1581. **Path Mapping**: Map container's expected paths to tinsnip XDG structure 1592. **Port Injection**: Override container's ports with tinsnip allocation 1603. **User Override**: Force container to run as tinsnip service UID 1614. **Config Adaptation**: Transform tinsnip config to container's expected format 1625. **Environment Translation**: Convert tinsnip variables to container's expectations 163 164### Example: LLDAP (Identity Service) 165 166LLDAP is an external container with its own conventions that we adapt: 167 168```yaml 169# Third-party container adaptation 170services: 171 lldap: 172 image: lldap/lldap:latest-alpine-rootless 173 container_name: ${TIN_SERVICE_NAME:-lldap}-${TIN_SERVICE_ENVIRONMENT:-prod} 174 ports: 175 # Adapt: LLDAP's default ports → tinsnip port allocation 176 - "${PRIMARY_PORT}:3890" # LDAP protocol 177 - "${SECONDARY_PORT}:17170" # Web UI 178 volumes: 179 # Adapt: LLDAP expects /data → map to tinsnip XDG structure 180 - ${XDG_DATA_HOME}/${TIN_NAMESPACE}/${TIN_SERVICE_NAME}:/data 181 - /etc/timezone:/etc/timezone:ro 182 - /etc/localtime:/etc/localtime:ro 183 user: "${TIN_SERVICE_UID}:${TIN_SERVICE_UID}" 184 environment: 185 # Adapt: Translate tinsnip config to LLDAP's expected variables 186 - LLDAP_JWT_SECRET=changeme-jwt-secret-32-chars-min 187 - LLDAP_KEY_SEED=changeme-key-seed-32-chars-minimum 188 - LLDAP_BASE_DN=dc=home,dc=local 189 - LLDAP_LDAP_USER_DN=admin 190 - LLDAP_LDAP_USER_PASS=changeme-admin-password 191 - LLDAP_DATABASE_URL=sqlite:///data/users.db 192 restart: unless-stopped 193 networks: 194 - tinsnip_network 195 196networks: 197 tinsnip_network: 198 external: true 199``` 200 201## Service Deployment 202 203### Deployment Process 204 2051. **Prepare Infrastructure**: 206 ```bash 207 ./machine/setup.sh myservice prod nas-server 208 ``` 209 2102. **Deploy Service**: 211 ```bash 212 # Switch to service user 213 sudo -u myservice-prod -i 214 215 # Copy from service catalog or create locally 216 cp -r ~/.local/opt/dynamicalsystem.service/myservice /mnt/docker/service/ 217 cd /mnt/docker/service/myservice 218 219 # Run setup if present 220 [[ -f setup.sh ]] && ./setup.sh 221 222 # Deploy 223 docker compose up -d 224 ``` 225 2263. **Verify Deployment**: 227 ```bash 228 docker compose ps 229 docker compose logs -f 230 ``` 231 232### Service Management 233 234```bash 235# Status check 236docker compose ps 237 238# View logs 239docker compose logs -f [service-name] 240 241# Restart service 242docker compose restart 243 244# Update service 245docker compose pull 246docker compose up -d 247 248# Stop service 249docker compose down 250``` 251 252### Data Access 253 254**Direct NFS Mount Access:** 255```bash 256# Access service data directly on NFS mounts 257ls /mnt/tinsnip/data/dynamicalsystem/myservice # Application data 258ls /mnt/tinsnip/config/dynamicalsystem/myservice # Configuration 259ls /mnt/tinsnip/state/dynamicalsystem/myservice # State/logs 260``` 261 262**Note**: With the new direct mount approach, XDG paths point directly to NFS mounts via the .env file, eliminating the need for symlinks. 263 264## Validation Checklist 265 266Before deploying, verify your service: 267 268### Volume Configuration 269- [ ] No named volumes in `volumes:` section 270- [ ] All volumes use XDG environment variables 271- [ ] Volumes map to appropriate container paths 272- [ ] XDG paths resolve to NFS-backed directories 273 274### User and Permissions 275- [ ] Service specifies `user: "${TIN_SERVICE_UID}:${TIN_SERVICE_UID}"` 276- [ ] Container processes run as non-root 277- [ ] File permissions work with tinsnip UID 278 279### Port Configuration 280- [ ] Ports use environment variables (`${PRIMARY_PORT}`, etc.) 281- [ ] No hardcoded port numbers 282- [ ] Port allocation fits within UID range (UID to UID+9) 283 284### Network Configuration 285- [ ] Service connects to `tinsnip_network` 286- [ ] Network is marked as `external: true` 287- [ ] Inter-service communication uses service names 288 289### Environment Variables 290- [ ] Uses tinsnip-provided variables where appropriate 291- [ ] No hardcoded values that should be dynamic 292- [ ] Secrets loaded from files, not environment variables 293 294### XDG Integration 295- [ ] Volumes reference XDG environment variables 296- [ ] Paths follow XDG Base Directory specification 297- [ ] Data accessible through both XDG and direct paths 298 299## Troubleshooting 300 301### Data Not Persisting 302**Problem**: Data lost after `docker compose down` 303**Solution**: Check for named volumes, ensure XDG bind mounts 304 305### Permission Denied 306**Problem**: Container can't write to mounted directories 307**Solution**: Verify `user:` directive and NFS mount permissions 308 309### XDG Paths Not Working 310**Problem**: XDG symlinks broken or missing 311**Solution**: Re-run machine setup to recreate XDG symlinks 312 313### Port Conflicts 314**Problem**: Service won't start, port already in use 315**Solution**: Check environment variable usage, verify UID calculation 316 317### Config Not Loading 318**Problem**: Third-party service ignoring configuration 319**Solution**: Verify config file paths match container expectations