---
title: Docker
description: Guide to deploying Afilmory via Docker.
createdAt: 2025-07-20T22:35:03+08:00
lastModified: 2025-11-14T00:23:27+08:00
---

# Docker Deployment

![Docker Deployment](https://github.com/Afilmory/assets/blob/main/docker-deploy.webp?raw=true)

## TL;DR

Fork https://github.com/Afilmory/docker, customize configuration and build image.

## 🚀 Quick Start

Using Docker to deploy Afilmory is a convenient and efficient way to manage your photo gallery application. This guide will walk you through the steps to set up and run Afilmory using Docker.

### Prerequisites

- Docker
- Docker Compose (optional but recommended)
- PostgreSQL database (for SSR app)

### 1. Configuration Files

Before building your Docker image, you'll need to configure the following files:

**`config.json`**

```json
{
  "name": "Your Photo Gallery",
  "title": "Your Photo Gallery",
  "description": "Capturing beautiful moments in life",
  "url": "https://your-domain.com",
  "accentColor": "#fb7185",
  "author": {
    "name": "Your Name",
    "url": "https://your-website.com",
    "avatar": "https://your-avatar-url.com/avatar.png"
  },
  "social": {
    "twitter": "@yourusername"
  },
  "extra": {
    "accessRepo": true
  }
}
```

**`builder.config.ts`**

```ts
import { defineBuilderConfig, githubRepoSyncPlugin } from '@afilmory/builder'

export default defineBuilderConfig(() => ({
  storage: {
    provider: 's3',
    bucket: 'your-photos-bucket',
    region: 'us-east-1',
    prefix: 'photos/',
    customDomain: 'cdn.yourdomain.com',
  },
  plugins: [
    githubRepoSyncPlugin({
      repo: {
        enable: true,
        url: 'https://github.com/username/gallery-public',
        token: process.env.GIT_TOKEN,
        branch: process.env.BUILDER_REPO_BRANCH ?? 'main',
      },
    }),
  ],
  performance: {
    worker: {
      enabled: true,
      maxWorkers: 4,
    },
  },
}))
```

If you keep assets on a non-`main` branch, set `branch` accordingly; the plugin will also auto-create the specified branch when it doesn’t exist yet so first pushes succeed even for empty repositories.

**`.env`**

```bash
# Database Configuration
PG_CONNECTION_STRING=postgresql://user:password@postgres:5432/afilmory

# S3 Storage Configuration
S3_ACCESS_KEY_ID=your_access_key_id
S3_SECRET_ACCESS_KEY=your_secret_access_key

# Git Repository (optional)
GIT_TOKEN=your_github_token

# Application Settings
NODE_ENV=production
PORT=3000
```

### 2. Dockerfile setup

Create a `Dockerfile` in your project root:

```dockerfile
# Dockerfile for Next.js app in a pnpm monorepo
# This Dockerfile should be built from the root of the monorepo:
# > docker build -t iris-ssr .
# > docker run -p 3000:3000 iris-ssr

# -----------------
# Base stage
# -----------------
FROM node:20-alpine AS base
WORKDIR /app
RUN corepack enable

# -----------------
# Builder stage
# -----------------
FROM base AS builder

RUN apk update && apk add --no-cache git perl

RUN git clone https://github.com/Afilmory/Afilmory --depth 1 .
COPY config.json ./
COPY builder.config.ts ./
COPY .env ./

ARG S3_ACCESS_KEY_ID
ARG S3_SECRET_ACCESS_KEY
ARG GIT_TOKEN
ARG PG_CONNECTION_STRING

RUN sh ./scripts/preinstall.sh
# Install all dependencies
RUN pnpm install --frozen-lockfile

# Build the app.
# The build script in the ssr package.json handles building the web app first.
RUN pnpm --filter=@afilmory/ssr build

# -----------------
# Runner stage
# -----------------
FROM base AS runner

WORKDIR /app

ENV NODE_ENV=production
# ENV PORT and other configurations are now in the config files
# and passed through environment variables during runtime.
RUN apk add --no-cache curl wget
# Create a non-root user
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

USER nextjs

COPY --from=builder --chown=nextjs:nodejs /app/apps/ssr/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/apps/ssr/.next/static /app/apps/ssr/.next/static
COPY --from=builder --chown=nextjs:nodejs /app/apps/ssr/public /app/apps/ssr/public

# The standalone output includes the server.js file.
# The PORT environment variable is automatically used by Next.js.
EXPOSE 3000

CMD ["node", "apps/ssr/server.js"]
```

### 3. Docker Compose Setup

Create a `docker-compose.yml` file in your project root:

```yaml
version: '3.8'

services:
  afilmory:
    build: .
    ports:
      - '3000:3000'
    environment:
      - NODE_ENV=production
    volumes:
      - ./config.json:/app/config.json:ro
      - ./builder.config.ts:/app/builder.config.ts:ro
      - ./.env:/app/.env:ro
    depends_on:
      - postgres
    restart: unless-stopped

  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: afilmory
      POSTGRES_USER: afilmory
      POSTGRES_PASSWORD: your_secure_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  postgres_data:
```

### 4. Building and Running

#### Option 1: Using Docker Compose (Recommended)

```bash
# Build and start all services
docker-compose up -d

# View logs
docker-compose logs -f afilmory

# Stop services
docker-compose down
```

#### Option 2: Manual Docker Build

```bash
# Build the image
docker build -t afilmory .

# Run the container
docker run -d \
  --name afilmory \
  -p 3000:3000 \
  --env-file .env \
  -v $(pwd)/config.json:/app/config.json:ro \
  -v $(pwd)/builder.config.ts:/app/builder.config.ts:ro \
  afilmory
```

## 📋 Configuration Details

### Storage Providers

Afilmory supports multiple storage providers. Configure them in `builder.config.ts`:

**S3-Compatible Storage:**

```json
{
  "storage": {
    "provider": "s3",
    "bucket": "your-bucket",
    "region": "us-east-1",
    "prefix": "photos/",
    "customDomain": "cdn.example.com"
  }
}
```

**GitHub Storage:**

```json
{
  "storage": {
    "provider": "github",
    "owner": "username",
    "repo": "photo-storage",
    "branch": "main",
    "path": "photos/"
  }
}
```

### Performance Tuning

For optimal performance in Docker environments:

```json
{
  "performance": {
    "worker": {
      "enabled": true,
      "maxWorkers": 4
    },
    "cache": {
      "thumbnails": true,
      "manifests": true
    }
  }
}
```
