homelab infrastructure services
Example Development Workflows#
This guide demonstrates practical workflows for teams using tinsnip with cross-platform development environments.
Scenario Setup#
Hardware & Users#
- u1: Developer on Windows
- u2: Developer on macOS
- s1: Ubuntu server (shared development environment)
- DS412plus: Synology NAS for persistent storage
Service Architecture#
Production Services (E=0):
- p1-prod (10100): Core service
- p2-prod (10200): API service
- p3-prod (10300): Database service
Test Services (E=1):
- t3-test (10310): Test instance of p3
Development Work:
- u1 creating d4 service (depends on p2-prod and t3-test)
- u2 modifying t3 service
Prerequisites#
On s1 (Ubuntu Server)#
# Install tinsnip
curl -fsSL "https://tangled.sh/dynamicalsystem.com/tinsnip/raw/main/install.sh?$(date +%s)" | bash
cd ~/.local/opt/dynamicalsystem.tinsnip
# Create user accounts for developers
sudo useradd -m -s /bin/bash -G sudo,docker u1
sudo useradd -m -s /bin/bash -G sudo,docker u2
sudo passwd u1
sudo passwd u2
# Fix npm permissions for each user
for user in u1 u2; do
sudo -u $user -i bash << 'EOF'
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
EOF
done
On Developer Machines#
Windows (u1) - PowerShell:
# Install VSCode
winget install Microsoft.VisualStudioCode
# Install Remote-SSH extension
code --install-extension ms-vscode-remote.remote-ssh
# Configure SSH
New-Item -Path ~/.ssh/config -ItemType File -Force
Add-Content ~/.ssh/config @"
Host s1-dev
HostName s1.example.com
User u1
LocalForward 10420 localhost:10420
LocalForward 10421 localhost:10421
LocalForward 10200 localhost:10200
LocalForward 10350 localhost:10350
"@
macOS (u2) - Terminal:
# Install VSCode
brew install --cask visual-studio-code
# Install Remote-SSH extension
code --install-extension ms-vscode-remote.remote-ssh
# Configure SSH
cat >> ~/.ssh/config << EOF
Host s1-dev
HostName s1.example.com
User u2
LocalForward 10330 localhost:10330
LocalForward 10331 localhost:10331
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
EOF
Workflow 1: u1 Creates New Service (d4)#
Step 1: Create Service Infrastructure#
# SSH to s1
ssh s1-dev
# Create d4 development environment
cd ~/.local/opt/dynamicalsystem.tinsnip
tin machine create d4 dev DS412plus
# Output:
# Service user: d4-dev
# UID: 10420
# Ports: 10420-10429
# NFS mount: /mnt/d4-dev
Step 2: Set Up Service Code#
# As u1 on s1
cd /mnt/d4-dev/service
# Create service structure
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
d4:
build: .
container_name: d4-dev
ports:
- "10420:8000"
- "10421:8001" # Admin UI
environment:
- TIN_SERVICE_NAME=d4
- TIN_SERVICE_ENV=dev
- TIN_SERVICE_UID=10420
- P2_API_URL=http://p2-prod:10200
- T3_DB_URL=postgres://t3-test:10310/d4_dev
volumes:
- ./src:/app/src:delegated # Hot reload
- /mnt/d4-dev/data:/data
- /mnt/d4-dev/config:/config
networks:
- tinsnip-network
networks:
tinsnip-network:
external: true
EOF
# Create Dockerfile
cat > Dockerfile << 'EOF'
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["npm", "run", "dev"]
EOF
Step 3: VSCode Development#
On Windows (u1):
# Open VSCode with Remote-SSH
code --folder-uri vscode-remote://ssh-remote+s1-dev/mnt/d4-dev/service
# This opens VSCode connected to s1
# All file edits happen directly on s1
# Terminal runs on s1
Step 4: Claude Code Integration#
In VSCode integrated terminal (running on s1):
# Start Claude Code in the service directory
cd /mnt/d4-dev/service
claude code
# Ask Claude Code to help with the service
# "Create an Express API that connects to p2 service and t3 database"
# "Add error handling for the external service calls"
# "Generate TypeScript interfaces for the API responses"
Step 5: Test Service#
# In VSCode terminal (on s1)
docker compose up -d
docker compose logs -f
# In Windows browser (port forwarded via SSH)
# http://localhost:10420 - Main service
# http://localhost:10421 - Admin UI
Workflow 2: u2 Modifies Existing Service (t3)#
Step 1: Create Staging Environment#
# SSH to s1
ssh s1-dev
# Create t3 staging environment for modifications
cd ~/.local/opt/dynamicalsystem.tinsnip
tin machine create t3 staging DS412plus
# Output:
# Service user: t3-staging
# UID: 10330
# Ports: 10330-10339
Step 2: Copy Current t3 Code#
# Clone t3 from test to staging
sudo cp -r /mnt/t3-test/service/* /mnt/t3-staging/service/
sudo chown -R t3-staging:t3-staging /mnt/t3-staging/service
Step 3: VSCode Development#
On macOS (u2):
# Open VSCode with Remote-SSH
code --folder-uri vscode-remote://ssh-remote+s1-dev/mnt/t3-staging/service
Step 4: Make Modifications with Claude Code#
In VSCode integrated terminal:
cd /mnt/t3-staging/service
claude code
# Work with Claude Code
# "Add caching layer to the database queries"
# "Implement connection pooling"
# "Add metrics endpoint for Prometheus"
Step 5: Test Changes#
# Build and run
docker compose build --no-cache
docker compose up -d
# Run tests
docker compose exec t3 npm test
# Check metrics endpoint
curl http://localhost:10332/metrics
Workflow 3: Integration Testing#
Create Integration Environment#
# On s1, create shared integration environment
tin machine create integration demo DS412plus
# UID: 10440, Ports: 10440-10449
Deploy Both Services#
# Deploy u2's t3 changes
cd /mnt/integration-demo/service
cp -r /mnt/t3-staging/service/t3 ./
cd t3
docker compose up -d
# Deploy u1's d4 service pointing to new t3
cd /mnt/integration-demo/service
cp -r /mnt/d4-dev/service/d4 ./
cd d4
# Update d4 to use staging t3
sed -i 's/t3-test:10310/t3-staging:10330/g' docker-compose.yml
docker compose up -d
Run Integration Tests#
# Create test script
cat > /mnt/integration-demo/test.sh << 'EOF'
#!/bin/bash
echo "Testing d4 -> t3 integration..."
# Test d4 API
curl -f http://localhost:10440/health || exit 1
# Test t3 connection
curl -f http://localhost:10440/api/database/status || exit 1
echo "Integration tests passed!"
EOF
chmod +x /mnt/integration-demo/test.sh
./test.sh
Workflow 4: Collaborative Debugging#
Using tmux for Shared Sessions#
# u1 starts tmux session on s1
ssh s1-dev
tmux new -s debug-d4
# Split panes
# Ctrl-b % (vertical split)
# Ctrl-b " (horizontal split)
# Pane 1: Docker logs
docker compose logs -f d4
# Pane 2: Claude Code
claude code
# Pane 3: Testing
watch curl http://localhost:10420/health
# u2 joins the session
ssh s1-dev
tmux attach -t debug-d4
# Both developers see the same screen
# Can take turns typing
Shared Claude Code Context#
# Create shared context file
cat > /mnt/integration-demo/CLAUDE.md << 'EOF'
# Integration Context
## Services
- d4: New service by u1, ports 10420-10429
- t3: Modified by u2, ports 10330-10339
## Dependencies
- d4 -> p2 (10200) for API
- d4 -> t3 (10330) for database
## Current Issues
- Connection timeout between d4 and t3
- Need to add retry logic
## Test Command
curl http://localhost:10440/api/test
EOF
# Both developers can reference
cd /mnt/integration-demo
claude code
# "Read CLAUDE.md and help debug the connection timeout"
Workflow 5: Service Promotion#
Test to Production Pipeline#
# 1. Freeze staging changes
cd /mnt/t3-staging/service
git add .
git commit -m "Add caching and connection pooling"
git push origin t3-staging
# 2. Deploy to test environment
tin machine create t3 test DS412plus
cd /mnt/t3-test/service
git pull origin t3-staging
docker compose up -d
# 3. Run test suite
docker compose exec t3 npm run test:integration
# 4. Promote to production (if tests pass)
cd /mnt/t3-prod/service
git pull origin t3-staging
docker compose up -d --scale t3=2 # Rolling update
Tips and Tricks#
1. Port Forwarding Alias#
Windows PowerShell:
function Connect-Dev {
ssh -L 10420:localhost:10420 `
-L 10421:localhost:10421 `
-L 10200:localhost:10200 `
-L 10350:localhost:10350 `
u1@s1.example.com
}
macOS/Linux Bash:
alias s1dev='ssh -L 10330:localhost:10330 -L 10331:localhost:10331 s1-dev'
2. Quick Service Status#
# Create status script
cat > ~/status.sh << 'EOF'
#!/bin/bash
echo "=== Service Status ==="
for service in d4-dev t3-staging integration-demo; do
if [[ -d /mnt/$service/service ]]; then
echo -n "$service: "
cd /mnt/$service/service
docker compose ps --format "table {{.State}}" | tail -n1
fi
done
EOF
chmod +x ~/status.sh
3. Claude Code Templates#
# Create templates for common tasks
mkdir -p ~/.claude-templates
cat > ~/.claude-templates/debug-docker.md << 'EOF'
Analyze these Docker logs and identify the issue:
[PASTE LOGS]
Service configuration:
```yaml
[PASTE docker-compose.yml]
EOF
Use template#
cat ~/.claude-templates/debug-docker.md | claude code
### 4. VSCode Workspace Settings
Create `.vscode/settings.json` in each service:
```json
{
"terminal.integrated.cwd": "/mnt/d4-dev/service",
"files.watcherExclude": {
"**/node_modules/**": true,
"**/data/**": true
},
"docker.host": "unix:///var/run/docker.sock",
"remote.autoForwardPorts": true,
"remote.autoForwardPortsSource": "process"
}
Troubleshooting#
Permission Denied on Docker#
# Add user to docker group
sudo usermod -aG docker u1
newgrp docker
NFS Mount Issues#
# Check NFS exports on NAS
ssh admin@DS412plus
showmount -e localhost
# Test mount manually
sudo mount -t nfs DS412plus:/volume1/topsheet/gazette/test /tmp/test
Port Already in Use#
# Find process using port
sudo lsof -i :10420
sudo netstat -tulpn | grep 10420
# Kill process if needed
sudo kill $(sudo lsof -t -i:10420)
Claude Code Not Found#
If claude code command is not available:
- Ensure you're using the desktop app or have API access configured
- Alternative: Use Claude.ai web interface with copy/paste
- Or use API integration scripts as shown in the examples
Summary#
This workflow enables:
- Cross-platform development (Windows/macOS developers on Linux server)
- Service isolation using tinsnip's SMEP convention
- Fast iteration with VSCode Remote-SSH and Docker
- AI assistance with Claude Code integration
- Predictable networking with SMEP port allocation
- Persistent storage via NFS mounts
The key is using s1 as a shared development server with isolated environments per developer and service, while maintaining local IDE experience through VSCode Remote-SSH.