{"id":35163913,"url":"https://github.com/ducks/burrow-systemd","last_synced_at":"2026-05-19T17:39:41.188Z","repository":{"id":329814663,"uuid":"1120704016","full_name":"ducks/burrow-systemd","owner":"ducks","description":"systemd based setup for my Fornex VPS","archived":false,"fork":false,"pushed_at":"2025-12-22T20:32:41.000Z","size":23,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-23T08:53:01.104Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ducks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-21T19:14:30.000Z","updated_at":"2025-12-22T20:32:45.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ducks/burrow-systemd","commit_stats":null,"previous_names":["ducks/burrow-systemd"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/ducks/burrow-systemd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fburrow-systemd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fburrow-systemd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fburrow-systemd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fburrow-systemd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ducks","download_url":"https://codeload.github.com/ducks/burrow-systemd/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ducks%2Fburrow-systemd/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28102735,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-28T02:00:05.685Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-28T19:03:27.562Z","updated_at":"2026-05-19T17:39:41.168Z","avatar_url":"https://github.com/ducks.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Burrow - Systemd-based Infrastructure\n\nDeclarative infrastructure for VPS using systemd services instead of Docker.\n\n## Services\n\n- **Caddy** - Reverse proxy with automatic HTTPS (Let's Encrypt)\n- **Gitea** - Git hosting at code.jakegoldsborough.com\n- **Woodpecker CI** - Continuous integration at ci.jakegoldsborough.com\n- **Scrob** - Music scrobbling server at scrob.jakegoldsborough.com\n- **Scrob UI** - Web interface for Scrob at ui.scrob.jakegoldsborough.com\n- **GoatCounter** (2 instances) - Analytics at:\n  - stats.jakegoldsborough.com\n  - stats.date-ver.com\n- **PostgreSQL** - Database for Gitea, Woodpecker, and Scrob\n\n## Architecture\n\nUnlike the Docker version, this uses native systemd services:\n\n- All services run directly on the host (no containers)\n- PostgreSQL for Gitea, Woodpecker, and Scrob databases\n- SQLite for GoatCounter instances (separate databases)\n- Woodpecker uses local backend (no Docker required for builds)\n- Scrob UI served as static files by Caddy\n- Caddy handles reverse proxy and automatic SSL\n- Configuration managed via git\n\n## Prerequisites\n\n- VPS with root access\n- Arch Linux, Debian/Ubuntu, or Fedora/RHEL\n- Domains pointing to your VPS:\n  - code.jakegoldsborough.com\n  - ci.jakegoldsborough.com\n  - scrob.jakegoldsborough.com\n  - ui.scrob.jakegoldsborough.com\n  - stats.jakegoldsborough.com\n  - stats.date-ver.com\n\n## Directory Structure\n\n```\nburrow-systemd/\n├── systemd/              # Systemd service files\n│   ├── caddy.service\n│   ├── gitea.service\n│   ├── woodpecker-server.service\n│   ├── woodpecker-agent.service\n│   ├── scrob.service\n│   ├── goatcounter-jg.service\n│   └── goatcounter-dv.service\n├── config/\n│   ├── Caddyfile         # Reverse proxy config\n│   ├── gitea/\n│   │   └── app.ini       # Gitea configuration template\n│   ├── woodpecker/\n│   │   ├── server.env    # Woodpecker server config\n│   │   └── agent.env     # Woodpecker agent config\n│   ├── scrob/\n│   │   └── scrob.env     # Scrob server config\n│   └── postgres/\n│       └── init-databases.sql  # PostgreSQL initialization\n├── bin/\n│   ├── bootstrap         # Initial setup script\n│   ├── deploy            # Update and restart services\n│   ├── backup            # Backup databases\n│   ├── update            # Update service binaries\n│   └── create-scrob-user # Create scrob user and get API token\n├── data/                 # Local data (git-ignored)\n│   ├── goatcounter-jg/\n│   └── goatcounter-dv/\n├── .env                  # Secrets (git-ignored)\n└── .env.example          # Example environment file\n```\n\n## Installation\n\n### 1. Clone this repository on your VPS\n\n```bash\ncd ~/dev\ngit clone \u003cyour-repo-url\u003e burrow-systemd\ncd burrow-systemd\n```\n\n### 2. Run bootstrap\n\nThis will:\n- Install required packages (caddy, postgresql)\n- Download Gitea and GoatCounter binaries\n- Create system users and directories\n- Initialize PostgreSQL\n- Generate secure passwords\n- Copy configuration files\n- Install systemd services\n- Migrate existing GoatCounter databases if found\n\n```bash\nsudo ./bin/bootstrap\n```\n\n### 3. Review the generated configuration\n\n```bash\ncat .env  # Check generated passwords\n```\n\n### 4. Deploy services\n\n```bash\nsudo ./bin/deploy\n```\n\n### 5. Verify services are running\n\n```bash\nsystemctl status gitea caddy goatcounter-jg goatcounter-dv\n```\n\nYour services should now be available at:\n- https://code.jakegoldsborough.com\n- https://stats.jakegoldsborough.com\n- https://stats.date-ver.com\n\n## Setting Up Scrob\n\nScrob is a music scrobbling server with a REST API. After deployment, you'll need to create at least one user account.\n\n### 1. Create Your First User\n\nUse the `create-scrob-user` script to create a user and get an API token:\n\n```bash\ncd ~/dev/burrow-systemd\nsudo ./bin/create-scrob-user \u003cusername\u003e \u003cpassword\u003e\n```\n\nExample:\n```bash\nsudo ./bin/create-scrob-user jake mypassword\n```\n\nThe script will:\n1. Create the user in the scrob database\n2. Log in via the API\n3. Display your API token\n\n### 2. Save Your API Token\n\nCopy the displayed token and save it securely. You'll need it to use scrob clients like shelltrax:\n\n```bash\nexport SCROB_TOKEN=\"your-token-here\"\nexport SCROB_SERVER_URL=\"https://scrob.jakegoldsborough.com\"\n```\n\n### 3. Access Scrob\n\n- **API**: https://scrob.jakegoldsborough.com\n- **Web UI**: https://ui.scrob.jakegoldsborough.com\n\nLog in to the web UI with your username and password.\n\n### 4. Create Additional Users (Optional)\n\nRepeat the `create-scrob-user` command to create more users:\n\n```bash\nsudo ./bin/create-scrob-user alice alicespassword\nsudo ./bin/create-scrob-user bob bobspassword\n```\n\n## Setting Up Woodpecker CI\n\nWoodpecker CI requires OAuth integration with Gitea. Follow these steps:\n\n### 1. Create OAuth Application in Gitea\n\n1. Access Gitea at https://code.jakegoldsborough.com\n2. Log in and go to **Settings** → **Applications**\n3. Scroll to **Manage OAuth2 Applications**\n4. Click **Create a new OAuth2 Application**\n5. Fill in the details:\n   - **Application Name**: `Woodpecker CI`\n   - **Redirect URI**: `https://ci.jakegoldsborough.com/authorize`\n6. Click **Create Application**\n7. Copy the **Client ID** and **Client Secret**\n\n### 2. Configure Woodpecker\n\nEdit the `.env` file and add the OAuth credentials:\n\n```bash\ncd ~/dev/burrow-systemd\nnano .env\n```\n\nAdd or update these lines:\n\n```bash\nGITEA_OAUTH_CLIENT_ID=\u003cyour-client-id\u003e\nGITEA_OAUTH_CLIENT_SECRET=\u003cyour-client-secret\u003e\n```\n\n### 3. Start Woodpecker Services\n\n```bash\nsudo ./bin/deploy\n```\n\nThe deploy script will detect the OAuth configuration and start Woodpecker automatically.\n\n### 4. Access Woodpecker CI\n\nVisit https://ci.jakegoldsborough.com and log in with your Gitea account. Woodpecker will automatically sync your repositories.\n\n### 5. Configure Admin User (Optional)\n\nTo make yourself an admin, edit `config/woodpecker/server.env` and add your Gitea username:\n\n```bash\nWOODPECKER_ADMIN=your-username\n```\n\nThen redeploy:\n\n```bash\nsudo ./bin/deploy\n```\n\n## Deployment Workflow\n\nAfter making changes to configuration:\n\n```bash\n# 1. Make changes locally\nvim config/Caddyfile\n\n# 2. Commit to git\ngit add .\ngit commit -m \"Update Caddyfile\"\ngit push\n\n# 3. On VPS, pull changes\ncd ~/dev/burrow-systemd\ngit pull\n\n# 4. Deploy (updates config and restarts services)\nsudo ./bin/deploy\n```\n\n## Backup\n\nCreate a backup of all databases and data:\n\n```bash\nsudo ./bin/backup\n```\n\nThis backs up:\n- PostgreSQL databases (Gitea, Woodpecker, Scrob)\n- GoatCounter SQLite databases\n- Gitea repositories and data\n- Configuration files\n\nBackups are stored in `~/burrow-backups/` with timestamps.\n\n## Service Management\n\n### View logs\n\n```bash\n# All services\njournalctl -u gitea -u caddy -u scrob -u goatcounter-jg -u goatcounter-dv -u woodpecker-server -u woodpecker-agent -f\n\n# Individual service\njournalctl -u gitea -f\njournalctl -u caddy -f\njournalctl -u scrob -f\njournalctl -u woodpecker-server -f\njournalctl -u woodpecker-agent -f\n```\n\n### Restart a service\n\n```bash\nsudo systemctl restart gitea\nsudo systemctl restart caddy\nsudo systemctl restart scrob\nsudo systemctl restart woodpecker-server woodpecker-agent\n```\n\n### Stop all services\n\n```bash\nsudo systemctl stop gitea caddy scrob goatcounter-jg goatcounter-dv woodpecker-server woodpecker-agent\n```\n\n### Start all services\n\n```bash\nsudo systemctl start postgresql gitea scrob goatcounter-jg goatcounter-dv woodpecker-server woodpecker-agent caddy\n```\n\n## File Locations\n\n- Gitea data: `/var/lib/gitea/`\n- Gitea config: `/etc/gitea/app.ini`\n- Woodpecker data: `/var/lib/woodpecker/`\n- Woodpecker config: `/etc/woodpecker/server.env` and `/etc/woodpecker/agent.env`\n- Scrob data: `/var/lib/scrob/`\n- Scrob config: `/etc/scrob/scrob.env`\n- Scrob UI: `/var/www/scrob-ui/`\n- GoatCounter data: `/var/lib/goatcounter-jg/` and `/var/lib/goatcounter-dv/`\n- Caddy config: `/etc/caddy/Caddyfile`\n- Systemd services: `/etc/systemd/system/*.service`\n- PostgreSQL data: `/var/lib/postgres/data`\n\n## Troubleshooting\n\n### Services won't start\n\nCheck logs:\n```bash\njournalctl -u \u003cservice-name\u003e -n 50\n```\n\n### Port conflicts\n\nCheck what's using a port:\n```bash\nsudo netstat -tlnp | grep :80\nsudo netstat -tlnp | grep :443\nsudo netstat -tlnp | grep :3000\n```\n\n### PostgreSQL connection issues\n\nCheck if PostgreSQL is running:\n```bash\nsystemctl status postgresql\n```\n\nTest connection:\n```bash\nsudo -u postgres psql -c \"\\l\"\n```\n\n### Caddy certificate issues\n\nCheck Caddy logs:\n```bash\njournalctl -u caddy -f\n```\n\nMake sure ports 80 and 443 are open and domains are pointing to your VPS.\n\n## Security Notes\n\n- `.env` file contains sensitive credentials - keep it secure and backed up\n- All services run as dedicated users with limited permissions\n- Caddy handles automatic HTTPS with Let's Encrypt\n- PostgreSQL is only accessible locally (not exposed to internet)\n- GoatCounter instances run on localhost and are proxied through Caddy\n\n## Differences from Docker Version\n\n**Advantages:**\n- No Docker networking issues\n- Direct systemd integration\n- Easier log access via journalctl\n- Services use system package manager where possible\n- Lower memory overhead\n\n**Disadvantages:**\n- Binaries must be manually updated\n- Service isolation is handled by systemd users, not containers\n- Can't easily move to a different host\n\n## Updates\n\n### Update Gitea\n\n```bash\n# Download new version\nwget -O /usr/local/bin/gitea https://dl.gitea.com/gitea/\u003cVERSION\u003e/gitea-\u003cVERSION\u003e-linux-amd64\nchmod +x /usr/local/bin/gitea\n\n# Restart service\nsudo systemctl restart gitea\n```\n\n### Update GoatCounter\n\n```bash\n# Download new version\nwget -O /tmp/goatcounter.gz https://github.com/arp242/goatcounter/releases/download/\u003cVERSION\u003e/goatcounter-\u003cVERSION\u003e-linux-amd64.gz\ngunzip /tmp/goatcounter.gz\nsudo mv /tmp/goatcounter /usr/local/bin/goatcounter\nsudo chmod +x /usr/local/bin/goatcounter\n\n# Restart services\nsudo systemctl restart goatcounter-jg goatcounter-dv\n```\n\n### Update Scrob\n\n```bash\n# Download new version\nwget -O /usr/local/bin/scrob https://github.com/ducks/scrob/releases/download/v\u003cVERSION\u003e/scrob-linux-x86_64\nchmod +x /usr/local/bin/scrob\n\n# Restart service\nsudo systemctl restart scrob\n```\n\n### Update Scrob UI\n\n```bash\n# Download new version\nwget -O /tmp/scrob-ui-dist.tar.gz https://github.com/ducks/scrob-ui/releases/download/v\u003cVERSION\u003e/scrob-ui-dist.tar.gz\ntar -xzf /tmp/scrob-ui-dist.tar.gz -C /var/www/scrob-ui\nrm /tmp/scrob-ui-dist.tar.gz\n\n# No restart needed - served as static files by Caddy\n```\n\n### Update Woodpecker\n\n```bash\n# Download new version\nwget -O /tmp/woodpecker-server https://github.com/woodpecker-ci/woodpecker/releases/download/v\u003cVERSION\u003e/woodpecker-server_linux_amd64\nwget -O /tmp/woodpecker-agent https://github.com/woodpecker-ci/woodpecker/releases/download/v\u003cVERSION\u003e/woodpecker-agent_linux_amd64\nsudo mv /tmp/woodpecker-server /usr/local/bin/woodpecker-server\nsudo mv /tmp/woodpecker-agent /usr/local/bin/woodpecker-agent\nsudo chmod +x /usr/local/bin/woodpecker-server /usr/local/bin/woodpecker-agent\n\n# Restart services\nsudo systemctl restart woodpecker-server woodpecker-agent\n```\n\nAlternatively, use the update script:\n\n```bash\nsudo ./bin/update woodpecker\n# or update all services\nsudo ./bin/update all\n```\n\n### Update Caddy\n\n```bash\n# Using package manager\nsudo pacman -Syu caddy  # Arch\nsudo apt-get update \u0026\u0026 sudo apt-get upgrade caddy  # Debian/Ubuntu\n\nsudo systemctl restart caddy\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fducks%2Fburrow-systemd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fducks%2Fburrow-systemd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fducks%2Fburrow-systemd/lists"}