Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions copier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,25 @@ supported_languages:
multiline: true
default: []

enable_ci_docker:
type: bool
help: Enable GitHub Actions workflow to build and push Docker images on release?
default: false
when: "{{ fqdn }}"

docker_registry:
type: str
help: Docker registry to push images to (e.g. registry.example.com:5050)
placeholder: registry.example.com:5050
default: ""
when: "{{ enable_ci_docker }}"

ci_runner_label:
type: str
help: GitHub Actions runner label (use self-hosted label for private registries)
default: "ubuntu-latest"
when: "{{ enable_ci_docker }}"

enable_watchtower:
type: bool
help: Do you want to enable watchtower (containers self-reload on update)?
Expand Down
65 changes: 44 additions & 21 deletions vibetuner-docs/docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,30 +413,53 @@ Consider:

## CI/CD Pipeline

### Basic GitHub Actions
### Automated Docker Builds (Scaffolded)

```yaml
# .github/workflows/deploy.yml
name: Deploy
on:
push:
tags:
- 'v*'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build and push Docker image
run: |
docker build -t myapp:${{ github.ref_name }} .
docker push myapp:${{ github.ref_name }}
- name: Deploy to server
run: |
# SSH into your server and pull the new image
ssh user@your-server "cd /app && docker compose pull && docker compose up -d"
When you enable `enable_ci_docker` during scaffolding, a GitHub Actions workflow is
generated that automatically builds and pushes Docker images when a release is
published (via Release Please).

**Scaffolding prompts:**

- `enable_ci_docker` — enables the workflow (requires `fqdn` to be set)
- `docker_registry` — registry to push to (e.g. `registry.example.com:5050`)
- `ci_runner_label` — defaults to `ubuntu-latest`; set to a self-hosted runner
label for private registries

**Required GitHub secrets** (if your registry needs authentication):

- `DOCKER_REGISTRY_USERNAME`
- `DOCKER_REGISTRY_PASSWORD`

The workflow uses your existing `compose.prod.yml` bake configuration, so it
builds the same multi-platform images (linux/amd64 + linux/arm64) as
`just release`.

#### Private Registries

If your registry isn't exposed to the internet, use a self-hosted GitHub
Actions runner on the same private network. Set `ci_runner_label` to your
runner's label (e.g. `self-hosted,private-network`) during scaffolding.
The workflow doesn't include any VPN or tunnel-specific configuration,
keeping it generic.

### Manual Build and Push

```bash
just release
```

Builds the production image and pushes to your container registry. Requires a
clean git state and a tagged commit.

### Manual Deployment

```bash
just deploy-latest user@your-server
```

Pulls the latest image on the remote host and restarts services via SSH.

## Next Steps

- Monitor application performance
Expand Down
27 changes: 27 additions & 0 deletions vibetuner-docs/docs/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2139,6 +2139,9 @@ vibetuner version [--app]
- `redis_url` – default empty. Used when background jobs are enabled.
- `mongodb_url` – default empty. MongoDB connection string written to `.env.local`.
- `database_url` – default empty. SQL database connection string (PostgreSQL, MySQL, MariaDB, SQLite) written to `.env.local`.
- `enable_ci_docker` – default `false`. Only prompted when `fqdn` is set; adds a GitHub Actions workflow that builds and pushes Docker images on release.
- `docker_registry` – default empty. Only prompted when `enable_ci_docker` is true. The registry to push images to (e.g. `registry.example.com:5050`).
- `ci_runner_label` – default `ubuntu-latest`. Only prompted when `enable_ci_docker` is true. Set to a self-hosted runner label for private registries.
- `enable_watchtower` – default `false`. Only prompted when `fqdn` is set; adds Watchtower service to production Docker Compose.

#### Updating Existing Projects
Expand Down Expand Up @@ -2178,6 +2181,30 @@ just release

This builds the production image and pushes to your container registry.

#### CI Docker Builds

When `enable_ci_docker` is enabled during scaffolding, a GitHub Actions workflow
(`docker.yml`) is generated. It triggers on `release: published` events (created by
Release Please) and:

1. Checks out the release tag
2. Sets up QEMU and Docker Buildx for multi-platform builds
3. Logs in to the configured `docker_registry` (skipped if no credentials are set)
4. Runs `docker buildx bake -f compose.prod.yml --push`

The workflow reuses the same bake configuration as `just release`, producing identical
multi-platform images (linux/amd64 + linux/arm64).

**Private registries:** Set `ci_runner_label` to a self-hosted runner label (e.g.
`self-hosted,private-network`) for registries not exposed to the internet. The runner
must be on the same network as the registry. No VPN or tunnel configuration is included
in the workflow.

**GitHub secrets** (if registry requires authentication):

- `DOCKER_REGISTRY_USERNAME`
- `DOCKER_REGISTRY_PASSWORD`

#### Environment Configuration

**Production Environment Variables**
Expand Down
3 changes: 3 additions & 0 deletions vibetuner-docs/docs/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ Important notes:
`override_config`, `vibetuner_db` pytest fixtures
- **Error Messages**: Actionable error messages with env var examples, Docker
commands, and documentation links
- **CI Docker Builds**: Optional GitHub Actions workflow (`enable_ci_docker` scaffolding
prompt) that builds and pushes multi-platform Docker images on release. Supports
private registries via self-hosted runner labels (`ci_runner_label`)

## Reference

Expand Down
54 changes: 54 additions & 0 deletions vibetuner-template/.github/workflows/docker.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{% if enable_ci_docker %}
name: Docker

on:
release:
types: [published]

env:
DOCKER_REGISTRY: {{ docker_registry }}
COMPOSE_PROJECT_NAME: {{ project_slug }}

jobs:
build-and-push:
runs-on: {{ ci_runner_label }}
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Derive version from release tag
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Check registry credentials
id: creds
run: |
if [ -n "$USERNAME" ]; then
echo "available=true" >> "$GITHUB_OUTPUT"
fi
env:
USERNAME: {% raw %}${{ secrets.DOCKER_REGISTRY_USERNAME }}{% endraw %}

- name: Log in to Docker registry
if: {% raw %}steps.creds.outputs.available == 'true'{% endraw %}
uses: docker/login-action@v3
with:
registry: {{ docker_registry }}
username: {% raw %}${{ secrets.DOCKER_REGISTRY_USERNAME }}{% endraw %}
password: {% raw %}${{ secrets.DOCKER_REGISTRY_PASSWORD }}{% endraw %}

- name: Build and push
env:
VERSION: {% raw %}${{ steps.version.outputs.version }}{% endraw %}
PYTHON_VERSION: {{ python_version }}
ENVIRONMENT: prod
run: docker buildx bake -f compose.prod.yml --push
{% endif %}
2 changes: 1 addition & 1 deletion vibetuner-template/.justfiles/helpers.justfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ _project-vars:
print(f"export COMPOSE_PROJECT_NAME={answers.get('project_slug', 'scaffolding').strip()}")
print(f"export FQDN={answers.get('fqdn', '').strip()}")
print(f"export ENABLE_WATCHTOWER={str(answers.get('enable_watchtower', False)).lower()}")
registry = os.environ.get('DOCKER_REGISTRY')
registry = os.environ.get('DOCKER_REGISTRY') or answers.get('docker_registry', '')
if registry:
print(f"export DOCKER_REGISTRY={registry.strip()}")
PYEOF
Loading