A reference implementation demonstrating how to build, version, and distribute self-hosted software using Self-Host Pro.
While this demo uses Laravel, Self-Host Pro works with any containerized application — Node.js, Python, Go, Ruby, or any stack that runs in Docker.
This app showcases the complete workflow for selling self-hosted software:
- Database Persistence — SQLite data survives container restarts
- Version Embedding — App version injected at build time via
composer.json - Automated Builds — GitHub Actions builds and pushes to Self-Host Pro's registry
- Release Strategies — Edge, pre-release, and stable release workflows
The CI/CD setup uses a single reusable workflow called by three trigger files. Check out .github/workflows/ to see:
| File | Trigger | Tags Produced |
|---|---|---|
action_stable-release.yml |
GitHub Release | latest, 1.2.0, 1 |
action_prerelease.yml |
GitHub Pre-release | prerelease, prerelease-v1.0.0-beta |
action_edge.yml |
Push to main |
edge-main |
service_docker-build-and-publish.yml |
Called by above | (reusable build logic) |
Each trigger workflow is ~20 lines — they just pass different tags to the shared build pipeline. Copy a trigger file to add new release channels like nightly or canary.
This project uses Spin for local Docker development. Spin provides a consistent environment that mirrors production.
- Docker & Docker Compose
- Spin
# Clone the repo
git clone https://github.com/selfhostpro/demo-app.git
cd demo-app
# Copy environment file
cp .env.example .env
# Install dependencies
spin run php composer install
spin run node yarn install
# Generate app key and run migrations
spin run php php artisan key:generate
spin run php php artisan migrate
# Seed demo data
spin run php php artisan initialize --force
# Start application servers with Vite
spin upVisit https://laravel.dev.test (add to 127.0.0.1 laravel.dev.test in /etc/hosts if needed).
The project includes two Dockerfiles:
Dockerfile.php— Production PHP image with FrankenPHPDockerfile.node— Node.js for building frontend assets
The GitHub Actions workflow handles production builds, but you can build manually:
# Install dependencies first
spin run php composer install --optimize-autoloader --no-dev
spin run node yarn install && yarn build
# Build the image
docker build -t shpcr.io/yourname/app:latest -f Dockerfile.php .
# Push to Self-Host Pro registry
docker login shpcr.io
docker push shpcr.io/yourname/app:latest├── .github/workflows/ # CI/CD workflows (start here!)
│ ├── action_stable-release.yml
│ ├── action_prerelease.yml
│ ├── action_edge.yml
│ └── service_docker-build-and-publish.yml
├── app/
│ ├── Console/Commands/ # Artisan commands
│ └── Models/ # Eloquent models
├── resources/
│ ├── css/ # Tailwind CSS
│ ├── js/ # Globe animation & app JS
│ └── views/ # Blade templates
├── docker-compose.yml # Base compose config
├── docker-compose.dev.yml # Development overrides
├── docker-compose.ci.yml # CI build config
├── Dockerfile.php # Production PHP image
└── Dockerfile.node # Node build image
Set the version in composer.json or via environment variable:
{
"version": "1.0.0"
}APP_VERSION=1.0.0The GitHub Actions workflow automatically updates composer.json with the release tag version before building.
# Seed space mission demo data
php artisan initialize [--force]
# Check app status
php artisan status
# Reset to clean state
php artisan reset [--force]