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
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
**/compose*
**/Dockerfile*
**/node_modules
!.next/standalone/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
**/.next
README.md
config/
k3d/
111 changes: 59 additions & 52 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
name: Docker

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Docker CI

on:
schedule:
Expand All @@ -13,7 +8,6 @@ on:
- main
- feature/**
- dev
# Publish semver tags as releases.
tags: [ 'v*.*.*' ]
paths-ignore:
- 'docs/**'
Expand All @@ -26,101 +20,117 @@ on:
merge_group:

env:
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}


jobs:
pre-commit:
name: Linting Checks
runs-on: ubuntu-22.04
steps:
-
name: Checkout repository
- name: Checkout repository
uses: actions/checkout@v4
-
name: Install python

- name: Install python
uses: actions/setup-python@v5
with:
python-version: 3.x
-
name: Check files

- name: Check files
uses: pre-commit/action@v3.0.1
-
name: Install pnpm

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
run_install: false
-
name: Install Node.js

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
-
name: Install dependencies

- name: Install dependencies
run: pnpm install
-
name: Lint frontend

- name: Lint frontend
run: pnpm run lint

build:
name: Docker Build & Push
if: github.repository == 'gethomepage/homepage'
if: github.repository == 'gethomepage/homepage'
runs-on: self-hosted
needs:
- pre-commit
needs: [ pre-commit ]
permissions:
contents: read
packages: write
# This is used to complete the identity challenge
# with sigstore/fulcio when running outside of PRs.
id-token: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

# Login to Docker Registry
# https://github.com/docker/login-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.IMAGE_NAME }}
ghcr.io/${{ env.IMAGE_NAME }}
flavor: |
latest=auto

- name: Next.js build cache
uses: actions/cache@v4
with:
path: .next/cache
key: nextjs-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}-${{ hashFiles('**/*.js', '**/*.jsx') }}
restore-keys: |
nextjs-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
run_install: false

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'

- name: Install dependencies
run: pnpm install

- name: Build app
run: |
NEXT_PUBLIC_BUILDTIME="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}" \
NEXT_PUBLIC_VERSION="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}" \
NEXT_PUBLIC_REVISION="${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}" \
pnpm run build

- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Login to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

# Setup QEMU
# https://github.com/marketplace/actions/docker-setup-buildx#with-qemu
- name: Setup QEMU
uses: docker/setup-qemu-action@v3.6.0

# Workaround: https://github.com/docker/build-push-action/issues/461
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v3

# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.IMAGE_NAME }}
ghcr.io/${{ env.IMAGE_NAME }}
flavor: |
latest=auto

# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v6
Expand All @@ -130,18 +140,15 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
CI=true
BUILDTIME=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}
VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
REVISION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}
# https://github.com/docker/setup-qemu-action#about
# platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6
platforms: linux/amd64,linux/arm64
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max

# Temp fix
# https://github.com/docker/build-push-action/issues/252
# https://github.com/moby/buildkit/issues/1896
# https://github.com/docker/build-push-action/issues/252 / https://github.com/moby/buildkit/issues/1896
- name: Move cache
run: |
rm -rf /tmp/.buildx-cache
Expand Down
76 changes: 36 additions & 40 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,67 +1,63 @@
# Install dependencies only when needed
FROM docker.io/node:22-alpine AS deps

WORKDIR /app

COPY --link package.json pnpm-lock.yaml* ./

SHELL ["/bin/ash", "-xeo", "pipefail", "-c"]
RUN apk add --no-cache libc6-compat \
&& apk add --no-cache --virtual .gyp python3 make g++ \
&& npm install -g pnpm

RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store pnpm fetch | grep -v "cross-device link not permitted\|Falling back to copying packages from store"

RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store pnpm install -r --offline

# Rebuild the source code only when needed
FROM docker.io/node:22-alpine AS builder
# =========================
# Builder Stage
# =========================
FROM node:22-slim AS builder
WORKDIR /app

# Setup
RUN mkdir config
COPY . .

ARG CI
ARG BUILDTIME
ARG VERSION
ARG REVISION

COPY --link --from=deps /app/node_modules ./node_modules/
COPY . .

SHELL ["/bin/ash", "-xeo", "pipefail", "-c"]
RUN npm install -g pnpm \
&& pnpm run telemetry \
&& NEXT_PUBLIC_BUILDTIME=$BUILDTIME NEXT_PUBLIC_VERSION=$VERSION NEXT_PUBLIC_REVISION=$REVISION pnpm run build

# Production image, copy all the files and run next
FROM docker.io/node:22-alpine AS runner
LABEL org.opencontainers.image.title "Homepage"
LABEL org.opencontainers.image.description "A self-hosted services landing page, with docker and service integrations."
ENV CI=$CI

# Install and build only outside CI
RUN if [ "$CI" != "true" ]; then \
corepack enable && corepack prepare pnpm@latest --activate && \
pnpm install --frozen-lockfile --prefer-offline && \
NEXT_TELEMETRY_DISABLED=1 \
NEXT_PUBLIC_BUILDTIME=$BUILDTIME \
NEXT_PUBLIC_VERSION=$VERSION \
NEXT_PUBLIC_REVISION=$REVISION \
pnpm run build; \
else \
echo "✅ Using prebuilt app from CI context"; \
fi

# =========================
# Runtime Stage
# =========================
FROM node:22-alpine AS runner
LABEL org.opencontainers.image.title="Homepage"
LABEL org.opencontainers.image.description="A self-hosted services landing page, with docker and service integrations."
LABEL org.opencontainers.image.url="https://github.com/gethomepage/homepage"
LABEL org.opencontainers.image.documentation='https://github.com/gethomepage/homepage/wiki'
LABEL org.opencontainers.image.source='https://github.com/gethomepage/homepage'
LABEL org.opencontainers.image.licenses='Apache-2.0'

ENV NODE_ENV=production

# Setup
WORKDIR /app

# Copy files from context (this allows the files to copy before the builder stage is done).
COPY --link --chown=1000:1000 package.json next.config.js ./
# Copy some files from context
COPY --link --chown=1000:1000 /public ./public/

# Copy files from builder
COPY --link --from=builder --chown=1000:1000 /app/.next/standalone ./
COPY --link --from=builder --chown=1000:1000 /app/.next/static/ ./.next/static/
COPY --link --chmod=755 docker-entrypoint.sh /usr/local/bin/

# Copy only necessary files from the build stage
COPY --link --from=builder --chown=1000:1000 /app/.next/standalone/ ./
COPY --link --from=builder --chown=1000:1000 /app/.next/static/ ./.next/static

RUN apk add --no-cache su-exec

ENV NODE_ENV=production
ENV HOSTNAME=0.0.0.0
ENV PORT=3000
EXPOSE $PORT

HEALTHCHECK --interval=10s --timeout=3s --start-period=20s \
CMD wget --no-verbose --tries=1 --spider --no-check-certificate http://127.0.0.1:$PORT/api/healthcheck || exit 1
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:$PORT/api/healthcheck || exit 1

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["node", "server.js"]