Skip to content
Draft
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
59 changes: 59 additions & 0 deletions .github/workflows/build-images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Build and Push Multi-Architecture Images

on:
push:
branches: [ main ]
paths:
- 'docker/**'
- 'compose.yaml'
- 'lenny/**'
- 'requirements.txt'
- '.github/workflows/build-images.yml'
workflow_dispatch:

permissions:
contents: read
packages: write

jobs:
build-api:
name: Build lenny-api image
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

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

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

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}/lenny-api
tags: |
type=ref,event=branch
type=sha,prefix={{branch}}-sha-
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/api/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
155 changes: 155 additions & 0 deletions DOCKER_IMAGES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Docker Image Build Process

This document explains how Lenny uses pre-built multi-architecture Docker images to speed up deployment.

## Overview

Lenny uses a hybrid approach for Docker images to balance speed and flexibility:

- **Pre-built images**: API, Database (PostgreSQL), S3 Storage (MinIO), and Readium are pre-built
- **Local build**: The Reader service is built locally because it requires dynamic configuration

## Pre-built Images

The following services use pre-built multi-architecture (linux/amd64, linux/arm64) images:

### 1. lenny-api
- **Image**: `ghcr.io/archivelabs/lenny/lenny-api:latest`
- **Build trigger**: Automated via GitHub Actions on pushes to `main` branch
- **Why pre-built**: Static configuration, no build-time environment variables needed
- **Fallback**: If the image is unavailable, Docker Compose will build it locally

### 2. Database (PostgreSQL)
- **Image**: `postgres:16` (official Docker Hub image)
- **Why pre-built**: Official PostgreSQL image, no customization needed

### 3. S3 Storage (MinIO)
- **Image**: `minio/minio:latest` (official Docker Hub image)
- **Why pre-built**: Official MinIO image, no customization needed

### 4. Readium
- **Image**: `ghcr.io/readium/readium:0.6.3` (official Readium project image)
- **Why pre-built**: Official Readium image, no customization needed

## Local Build Images

### Reader (lenny-reader)
- **Build**: Always built locally via `docker/reader/Dockerfile`
- **Why local build**: Requires `NEXT_PUBLIC_MANIFEST_ALLOWED_DOMAINS` as a build argument
- This value changes when using custom domains or cloudflared tunnels
- Must be baked into the Next.js application at build time
- **Rebuild triggers**:
- Running `make tunnel` (adds cloudflared domain to allowed domains)
- Changing `NEXT_PUBLIC_MANIFEST_ALLOWED_DOMAINS` in `.env`

## GitHub Actions Workflow

The `.github/workflows/build-images.yml` workflow automatically builds and pushes the `lenny-api` image when:

1. Code is pushed to the `main` branch
2. Changes are made to:
- `docker/**` (Dockerfile changes)
- `compose.yaml` (Docker Compose configuration)
- `lenny/**` (application code)
- `requirements.txt` (Python dependencies)
3. The workflow is manually triggered via GitHub Actions UI

### Workflow Details

- **Platforms**: Builds for both `linux/amd64` and `linux/arm64`
- **Registry**: Images are pushed to GitHub Container Registry (ghcr.io)
- **Tags**:
- `latest` - always points to the most recent build from the main branch
- `main` - also points to the main branch (same as latest)
- `main-sha-<git-sha>` - specific commit on main branch (e.g., `main-sha-abc1234`)
- `<branch>-sha-<git-sha>` - specific commits on other branches (if workflow runs on them)
- **Caching**: Uses GitHub Actions cache for faster builds

## Benefits

1. **Faster initial setup**: Users can pull pre-built images instead of building from scratch
2. **Consistent images**: All users get the same tested images
3. **Multi-architecture support**: Works on both x86_64 and ARM64 (Apple Silicon, ARM servers)
4. **Reduced build time**: Only the reader service needs to be built locally

## For Users

### Updating to Latest Images

To pull the latest pre-built images:

```bash
make pull
```

This will update the API, database, S3, and Readium images to their latest versions.

## For Developers

### Testing the Build Workflow

You can test the GitHub Actions workflow locally using `act`:

```bash
# Install act (https://github.com/nektos/act)
# Run the workflow locally
act push -W .github/workflows/build-images.yml
```

### Manual Build and Push

If you need to manually build and push images (requires appropriate permissions):

```bash
# Login to GitHub Container Registry
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin

# Build and push multi-arch image
docker buildx create --use
docker buildx build \
--platform linux/amd64,linux/arm64 \
--push \
-t ghcr.io/archivelabs/lenny/lenny-api:latest \
-f docker/api/Dockerfile \
.
```

### Local Development

During development, you can force local builds by:

```bash
# Remove the pre-built image
docker rmi ghcr.io/archivelabs/lenny/lenny-api:latest

# Build locally
docker compose build api

# Or use the rebuild target
make rebuild
```

## Permissions

The GitHub Actions workflow requires the following permissions:
- `contents: read` - to checkout the repository
- `packages: write` - to push images to GitHub Container Registry

The `GITHUB_TOKEN` secret is automatically provided by GitHub Actions and has the necessary permissions.

### Repository Settings

To ensure the workflow can push images to GitHub Container Registry, verify that:

1. **Workflow permissions** are set correctly in the repository settings:
- Go to Settings > Actions > General > Workflow permissions
- Ensure "Read and write permissions" is selected
- Or use the specific permissions defined in the workflow file

2. **Package visibility**: After the first successful workflow run, you may want to make the package public:
- Go to the package page: `https://github.com/orgs/<YOUR_ORG>/packages/container/<repo>%2Flenny-api`
- For this repository, that would be: `https://github.com/orgs/ArchiveLabs/packages/container/lenny%2Flenny-api`
- Click "Package settings"
- Change visibility to "Public" (optional, but recommended for easier access)

3. **No additional secrets needed**: The workflow uses `GITHUB_TOKEN`, which is automatically available in GitHub Actions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ resetdb:
start:
@bash docker/utils/lenny.sh --start

# Pull latest pre-built images from GitHub Container Registry
.PHONY: pull
pull:
@echo "[+] Pulling latest pre-built images..."
@docker compose pull api db s3 readium

.PHONY: restart
restart:
@bash docker/utils/lenny.sh --restart
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ Lenny is a free, open source, Library-in-a-Box for libraries to preserve and len
---
## Technologies

- **Docker** for deployment and containerization
- **Docker** for deployment and containerization (with pre-built multi-architecture images - see [DOCKER_IMAGES.md](DOCKER_IMAGES.md))
- **nginx** as a reverse proxy
- **FastAPI** (Python) as the web & API framework
- **Minio** API for storing digital assets
Expand Down Expand Up @@ -110,6 +110,8 @@ To install and run Lenny as a production application:
curl -fsSL https://raw.githubusercontent.com/ArchiveLabs/lenny/refs/heads/main/install.sh | sudo bash
```

**Note**: Lenny now uses pre-built multi-architecture Docker images, making the initial setup significantly faster. The first time you run `make all`, Docker will pull optimized images instead of building everything from scratch. For more details, see [DOCKER_IMAGES.md](DOCKER_IMAGES.md).

---

## Development Setup
Expand All @@ -121,6 +123,8 @@ make all
```

- This will generate a `.env` file with reasonable defaults (if not present).
- Pre-built images for API, Database, S3, and Readium will be pulled automatically.
- Only the Reader service needs to be built locally (due to dynamic domain configuration).
- Navigate to `localhost:8080` (or your `$LENNY_PORT`).
- Enter the API container with:
`docker exec -it lenny_api bash`
Expand Down
8 changes: 8 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
services:

# API Service: Pre-built multi-architecture image from GitHub Container Registry
# Falls back to local build if image is not available or during development
api:
image: ghcr.io/archivelabs/lenny/lenny-api:latest
build:
context: .
dockerfile: docker/api/Dockerfile
Expand All @@ -23,6 +26,7 @@ services:
networks:
- lenny_network

# Database: Pre-built official PostgreSQL image from Docker Hub
db:
image: postgres:16
container_name: lenny_db
Expand All @@ -43,6 +47,7 @@ services:
networks:
- lenny_network

# S3 Storage: Pre-built official MinIO image from Docker Hub
s3:
image: minio/minio:latest
container_name: lenny_s3
Expand All @@ -63,6 +68,8 @@ services:
networks:
- lenny_network

# Reader: Built locally due to dynamic NEXT_PUBLIC_MANIFEST_ALLOWED_DOMAINS
# This needs to be rebuilt when using custom domains or cloudflared tunnels
reader:
container_name: lenny_reader
build:
Expand All @@ -84,6 +91,7 @@ services:
networks:
- lenny_network

# Readium: Pre-built multi-architecture image from Readium project
readium:
image: ghcr.io/readium/readium:0.6.3
container_name: lenny_readium
Expand Down