Experiment. Iterate. Elevate.
  • Rust 55.2%
  • Shell 26.6%
  • Python 9.6%
  • Makefile 3.8%
  • HTML 3.2%
  • Other 1.6%
Find a file
Mitchel Masius ff3a014f6f
All checks were successful
/ dbt (push) Successful in 49s
/ publish-docs (push) Successful in 29s
update pin-login
2026-04-15 09:29:52 +02:00
.forgejo/workflows fix: use correct registry namespace masium-public/skyduck 2026-03-04 10:55:19 +01:00
dbt-skyduck update docs 2026-03-06 16:29:19 +01:00
homeassistant add nginx, update cicd 2026-03-03 20:20:07 +01:00
masiusnl update README for masiusnl flask app 2026-02-26 11:47:33 +01:00
nextbytes Update the Nextbytes demo 2026-02-26 11:43:08 +01:00
zenbook-duo-kde update pin-login 2026-04-15 09:29:52 +02:00
README.md update README for skyduck 2026-03-04 11:04:37 +01:00

ByteLabs

Experiment. Iterate. Elevate.

A monorepo showcasing self-hosted projects with fully automated CI/CD via Forgejo Actions.

Every push to main triggers Forgejo Actions pipelines that:

  • Flask apps — build and tag Docker images (versioned as VERSION-SHORTSHA), push to a private container registry, SSH into the deployment server and run docker compose up -d
  • Home Assistant — call the HA REST API to pull the latest config from Git and reload all automations, scripts and scenes without a restart
  • dbt-Skyduck — run dbt models in a CI environment, retrieve dbt docs artifact, build and publish nginx image and deploy to Portainer to selfhost the dbt docs at skyduck.masius.nl.

No manual builds. No manual deployments.
Just commit, push, and let the pipeline handle the rest.

Projects

Project Description Port
masiusnl/ Minimal Flask app — redirects visitors to a configurable URL 5299
nextbytes/ Full Flask website with Traefik reverse-proxy integration 5099
homeassistant/ Git-managed Home Assistant config with Forgejo Actions CI/CD
dbt-skyduck/ Transavia route network analysis — dbt + DuckDB + OpenSky API skyduck.masius.nl

Architecture

Flask apps (masiusnl / nextbytes)

Forgejo push
    │
    ▼
Forgejo Actions workflow
    │
    ├─ Build Docker image
    ├─ Push to private registry
    │
    └─ SSH into server
           └─ docker compose pull + up -d

Reverse-proxy is handled by Traefik using container labels.
Images are stored in a private container registry (e.g. Forgejo Packages or any OCI-compliant registry).

Home Assistant

Forgejo push
    │
    ▼
Forgejo Actions workflow
    │
    └─ POST /api/services/shell_command/git_load  (HA REST API)
               │
               └─ git fetch + reset --hard origin/<branch>
                      │
                      └─ POST /api/services/homeassistant/reload_all

Config changes are live on HA within seconds of merging to main. Supports manual feature-branch deploys via workflow_dispatch.

dbt-skyduck

Forgejo push
    │
    ▼
Forgejo Actions workflow
    │
    ├─ PR   → dbt compile  (SQL syntax check, no data needed)
    │
    └─ main → dbt seed → dbt run → dbt test → dbt docs generate
                │
                ├─ DuckDB in-memory — no infrastructure required
                │
                └─ Docker build (nginx:alpine + target/ baked in)
                        │
                        └─ Push to registry → port 5199 on <your-host-ip>
                                                    │
                        Traefik (separate server) ──┘
                        routes skyduck.masius.nl via dynamic config

The dbt pipeline and Docker image build both run entirely inside the CI runner.

Repository Structure

bytelabs/
├── .forgejo/
│   └── workflows/
│       ├── build-masiusnl.yml       # CI/CD for masiusnl
│       ├── build-nextbytes.yml      # CI/CD for nextbytes
│       └── cleanup-old-images.yml   # Prune old images from registry (weekly)
├── masiusnl/
│   ├── Dockerfile.prod
│   ├── docker-compose.registry.yml
│   ├── gunicorn_conf.py             # Gunicorn config (port 5299)
│   ├── manage.py
│   ├── requirements.txt
│   ├── VERSION
│   ├── .dockerignore
│   └── project/
│       └── __init__.py              # Flask app — redirect to REDIRECT_URL
├── nextbytes/
│   ├── Dockerfile.prod
│   ├── docker-compose.registry.yml
│   ├── gunicorn_conf.py             # Gunicorn config (port 5099)
│   ├── manage.py
│   ├── requirements.txt
│   ├── Makefile                     # make dev / make install / etc.
│   ├── pyproject.toml               # Poetry dependencies
│   ├── VERSION
│   ├── .dockerignore
│   └── project/
│       ├── __init__.py              # Flask app — renders index.html
│       ├── templates/
│       │   └── index.html           # Website template
│       └── static/                  # CSS, JS, webfonts, images
└── homeassistant/
    ├── .forgejo/
    │   └── workflows/
    │       └── deploy-homeassistant.yml  # CI/CD — calls HA REST API
    ├── automations/
    │   ├── automation_git.yaml           # Pull latest config on HA startup
    │   ├── automation_lights.yaml        # Sunrise/sunset + presence lights
    │   ├── automation_notifications.yaml # Mobile push notifications
    │   └── automation_presence.yaml      # Device tracker → input_boolean
    ├── scripts/
    │   ├── git_load.sh                   # git fetch + reset + reload_all
    │   └── git_load_wrapper.sh           # Sets BRANCH env var, calls git_load.sh
    ├── configuration.yaml               # Main HA config — includes + shell_command
    ├── groups.yaml
    ├── scenes.yaml
    └── scripts.yaml
└── dbt-skyduck/
    ├── .forgejo/
    │   └── workflows/
    │       └── ci-dbt-skyduck.yml   # dbt CI + Docker publish
    ├── docker/
    │   ├── nginx.conf               # SPA fallback + cache headers
    │   └── docker-compose.yml       # Traefik labels for skyduck.masius.nl
    ├── ingest/
    │   └── fetch_flights.py             # OpenSky API fetcher
    ├── macros/
    │   └── generate_schema_name.sql     # clean DuckDB schema names
    ├── models/
    │   ├── staging/                     # stg__airports, stg__airlines, stg__routes, stg__opensky_flights
    │   ├── intermediate/                # int__transavia_routes_enriched, int__airport_connections
    │   └── marts/                       # mart__route_frequency, mart__hub_centrality, mart__top_destinations
    ├── seeds/
    │   ├── airports.csv                 # OpenFlights airports (30 Transavia-relevant airports)
    │   ├── airlines.csv                 # Transavia NL + Transavia France
    │   ├── routes.csv                   # ~63 Transavia routes
    │   └── opensky_flights_sample.csv   # 3-day flight sample (refresh with make fetch)
    ├── Dockerfile                       # nginx:alpine image for skyduck.masius.nl
    ├── dbt_project.yml
    ├── profiles.yml                     # dbt-duckdb (dev + ci targets)
    ├── packages.yml
    ├── pyproject.toml
    └── Makefile

Required Secrets

Configure the following secrets in your Forgejo/Gitea repository settings:

Flask apps (masiusnl, nextbytes):

Secret Description
REGISTRY_URL Hostname of your container registry (e.g. registry.example.com)
REGISTRY_USERNAME Registry login username
REGISTRY_PASSWORD Registry login password / token
SSH_HOST Hostname or IP of the deployment server
SSH_USER SSH username on the deployment server
SSH_PRIVATE_KEY SSH private key (PEM format)
SSH_PORT SSH port (default 22)

Home Assistant (homeassistant):

Secret Description
HA_TOKEN Long-Lived Access Token from HA (Profile → Security → Long-Lived Access Tokens)
Variable Description
HA_URL Full URL of your HA instance (e.g. http://192.168.x.x:8123) — set as a Forgejo repository variable

dbt-skyduck (dbt-skyduck):

Secret Description
REGISTRY_URL Hostname of your container registry
REGISTRY_USERNAME Registry login username
REGISTRY_PASSWORD Registry login password / token
PORTAINER_WEBHOOK_SKYDUCK Portainer stack webhook URL for automatic redeploy
PORTAINER_API_TOKEN Portainer API token (for image pruning)
PORTAINER_URL Portainer base URL (e.g. http://192.168.x.x:9000)

Required Environment Variables (on server)

Set these where docker compose runs (e.g. a .env file or systemd drop-in):

Variable Description Example
DOMAIN Domain name for Traefik routing example.com
REGISTRY_URL Same as the secret, used in compose file registry.example.com
IMAGE_TAG Image tag to deploy (injected by CI) 1.0-abc1234
REDIRECT_URL (masiusnl only) URL to redirect visitors to https://www.linkedin.com/

Adapting for Your Own Use

Flask apps:

  1. Replace myorg/ in the IMAGE_NAME workflow env vars with your registry org/username
  2. Add all required secrets in Forgejo repository settings
  3. Set DOMAIN on your deployment server
  4. For masiusnl: set REDIRECT_URL to your target redirect destination
  5. For nextbytes: customise project/templates/index.html and add assets to project/static/

Home Assistant:

  1. Add the HA_TOKEN secret in Forgejo repository settings
  2. Add HA_URL as a Forgejo repository variable (e.g. http://192.168.x.x:8123)
  3. Follow the full setup guide in homeassistant/README.md

dbt-skyduck:

  1. Add REGISTRY_URL, REGISTRY_USERNAME, REGISTRY_PASSWORD secrets in Forgejo repository settings
  2. Update skyduck.masius.nl in docker/docker-compose.yml to your own subdomain
  3. Point your Traefik dynamic config at <your-host-ip>:5199
  4. See dbt-skyduck/README.md for the full setup and fetch guide

Local Development

Flask apps — both masiusnl/ and nextbytes/ include a Makefile and pyproject.toml for Poetry-based development:

cd nextbytes/   # or masiusnl/
make install    # installs dependencies via Poetry
make dev        # starts Flask dev server with hot-reload

See each project's own README.md for the full command reference.

Home Assistant — no local dev server; use HA's built-in Developer Tools or the workflow_dispatch trigger to deploy a feature branch directly to your HA instance for testing.

dbt-skyduck — fully local, zero cloud dependencies:

cd dbt-skyduck/
make setup      # install deps + dbt packages
make fetch      # (optional) pull fresh flight data from OpenSky
make run        # seed + run all models
make docs       # generate + serve interactive dbt docs

Philosophy

ByteLabs exists to prototype, validate, and refine infrastructure patterns:

  • Container-first development
  • Automated CI/CD workflows
  • Reproducible builds
  • Zero-touch deployments

From commit to container — engineered to run anywhere.