Added basic use cases pages

This commit is contained in:
Arunavo Ray
2025-10-03 12:35:04 +05:30
parent b8dea1ee9c
commit 21e2f4717c
14 changed files with 1633 additions and 44 deletions

View File

@@ -22,6 +22,11 @@ Welcome to the Gitea Mirror documentation. This guide covers everything you need
- **[Sponsor Integration](./SPONSOR_INTEGRATION.md)** - GitHub Sponsors integration - **[Sponsor Integration](./SPONSOR_INTEGRATION.md)** - GitHub Sponsors integration
- **[Webhook Configuration](./WEBHOOKS.md)** - Set up GitHub webhooks - **[Webhook Configuration](./WEBHOOKS.md)** - Set up GitHub webhooks
### Marketing Site
- **Use Case Library** (`www/src/pages/use-cases/`) - MDX guides surfaced on the landing page use case section
- **Use Case Cards** (`www/src/lib/use-cases.ts`) - Central place to update titles, summaries, and CTA links shown across the site
### Architecture ### Architecture
- **[Architecture Overview](./ARCHITECTURE.md)** - System design and components - **[Architecture Overview](./ARCHITECTURE.md)** - System design and components

View File

@@ -1,13 +1,30 @@
# Astro with Tailwind # Gitea Mirror Marketing Site
```sh This Astro workspace powers the public marketing experience for Gitea Mirror. It includes the landing page, screenshots, call-to-action components, and the new use case library that highlights real-world workflows.
bun create astro@latest -- --template with-tailwindcss
## Developing Locally
```bash
bun install
bun run dev
``` ```
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/with-tailwindcss) The site is available at `http://localhost:4321`. Tailwind CSS v4 handles styling; classes can be used directly inside Astro, MDX, and React components.
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/with-tailwindcss)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/with-tailwindcss/devcontainer.json)
Astro comes with [Tailwind](https://tailwindcss.com) support out of the box. This example showcases how to style your Astro project with Tailwind. ## Project Structure
For complete setup instructions, please see our [Tailwind Integration Guide](https://docs.astro.build/en/guides/integrations-guide/tailwind). - `src/pages/index.astro` Main landing page
- `src/components/` Reusable UI (Header, Hero, Features, UseCases, etc.)
- `src/lib/use-cases.ts` Central data source for use case titles, summaries, and tags
- `src/pages/use-cases/` MDX guides for each use case, rendered with `UseCaseLayout`
- `src/layouts/UseCaseLayout.astro` Shared layout that injects the header, shader background, and footer into MDX guides
## Authoring Use Case Guides
1. Add or update a record in `src/lib/use-cases.ts`. This keeps the landing page and library listing in sync.
2. Create a new MDX file in `src/pages/use-cases/<slug>.mdx` with the `UseCaseLayout` layout and descriptive frontmatter.
3. Run `bun run dev` to preview the layout and ensure the new guide inherits global styles.
## Deployment
The marketing site is built with the standard Astro pipeline. Use `bun run build` to generate a production build before deploying.

View File

@@ -1,8 +1,8 @@
// @ts-check // @ts-check
import { defineConfig } from 'astro/config'; import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite'; import tailwindcss from '@tailwindcss/vite';
import react from '@astrojs/react'; import react from '@astrojs/react';
import mdx from '@astrojs/mdx';
// https://astro.build/config // https://astro.build/config
export default defineConfig({ export default defineConfig({
@@ -10,5 +10,5 @@ export default defineConfig({
plugins: [tailwindcss()] plugins: [tailwindcss()]
}, },
integrations: [react()] integrations: [react(), mdx()]
}); });

1037
www/bun.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -15,9 +15,10 @@ export function Header() {
}, []); }, []);
const navLinks = [ const navLinks = [
{ href: '#features', label: 'Features' }, { href: '/#features', label: 'Features' },
{ href: '#screenshots', label: 'Screenshots' }, { href: '/#use-cases', label: 'Use Cases' },
{ href: '#installation', label: 'Installation' } { href: '/#screenshots', label: 'Screenshots' },
{ href: '/#installation', label: 'Installation' }
]; ];
return ( return (
@@ -27,7 +28,7 @@ export function Header() {
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16"> <div className="flex items-center justify-between h-16">
{/* Logo */} {/* Logo */}
<a href="#" className="flex items-center gap-2 group"> <a href="/" className="flex items-center gap-2 group">
<img <img
src="/assets/logo.png" src="/assets/logo.png"
alt="Gitea Mirror Logo" alt="Gitea Mirror Logo"

View File

@@ -93,32 +93,6 @@ export function Hero() {
/> />
</a> </a>
</div> </div>
{/* Call to action buttons */}
{/* <div className="mt-8 sm:mt-10 flex flex-col sm:flex-row items-center justify-center gap-3 sm:gap-4 px-4 z-20">
<Button
size="lg"
className="relative group w-full sm:w-auto min-h-[48px] text-base bg-gradient-to-r from-primary to-accent hover:from-primary/90 hover:to-accent/90 shadow-lg shadow-primary/25 hover:shadow-xl hover:shadow-primary/30 transition-all duration-300"
asChild
>
<a
href="https://github.com/RayLabsHQ/gitea-mirror"
target="_blank"
rel="noopener noreferrer"
>
Get Started
<ArrowRight className="ml-2 h-4 w-4 transition-transform group-hover:translate-x-1" />
</a>
</Button>
<Button
size="lg"
variant="outline"
className="relative w-full sm:w-auto min-h-[48px] text-base border-primary/20 hover:bg-primary/10 hover:border-primary/30 hover:text-foreground transition-all duration-300"
asChild
>
<a href="#features">View Features</a>
</Button>
</div> */}
</div> </div>
</section> </section>
); );

View File

@@ -0,0 +1,64 @@
---
import { ArrowRight } from 'lucide-react';
import { useCases } from '@/lib/use-cases';
---
<section id="use-cases" class="py-16 sm:py-24 px-4 sm:px-6 lg:px-8 bg-muted/30 border-y">
<div class="max-w-7xl mx-auto">
<div class="text-center max-w-3xl mx-auto mb-12 sm:mb-16">
<span class="inline-flex items-center rounded-full border px-3 py-1 text-xs font-semibold uppercase tracking-widest text-muted-foreground mb-4">
Use Cases
</span>
<h2 class="text-2xl sm:text-3xl md:text-4xl font-bold tracking-tight">
Proven Ways Teams Depend on
<span class="text-gradient from-primary to-accent block sm:inline"> Gitea Mirror</span>
</h2>
<p class="mt-4 text-base sm:text-lg text-muted-foreground">
Explore real-world workflows where automated mirroring removes risk, accelerates migrations, and keeps engineering teams shipping.
</p>
</div>
<div class="grid gap-4 sm:gap-6 lg:gap-8 lg:grid-cols-3">
{useCases.map((useCase) => (
<article class="group relative flex flex-col rounded-2xl border bg-background/80 p-6 sm:p-8 shadow-sm transition-all duration-300 hover:-translate-y-1 hover:shadow-lg">
<div class="absolute inset-0 rounded-2xl bg-gradient-to-br from-primary/5 via-accent/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
<div class="relative flex flex-col h-full">
<h3 class="text-xl font-semibold mb-3">
{useCase.title}
</h3>
<p class="text-sm sm:text-base text-muted-foreground mb-4">
{useCase.summary}
</p>
<dl class="grid gap-3 text-sm sm:text-base text-muted-foreground">
<div>
<dt class="font-semibold text-foreground">Pain Point</dt>
<dd>{useCase.painPoint}</dd>
</div>
<div>
<dt class="font-semibold text-foreground">Outcome</dt>
<dd>{useCase.outcome}</dd>
</div>
</dl>
<div class="mt-6 flex flex-wrap gap-2">
{useCase.tags.map((tag) => (
<span class="inline-flex items-center rounded-full border border-muted px-3 py-1 text-xs font-medium uppercase tracking-wide text-muted-foreground">
{tag}
</span>
))}
</div>
<a
href={`/use-cases/${useCase.slug}/`}
class="mt-auto inline-flex items-center gap-2 pt-6 text-sm font-medium text-primary transition-colors hover:text-primary/80"
>
Read the playbook
<ArrowRight class="h-4 w-4 transition-transform group-hover:translate-x-1" />
</a>
</div>
</article>
))}
</div>
</div>
</section>

View File

@@ -0,0 +1,115 @@
---
import '../styles/global.css';
import { Header } from '../components/Header';
import Footer from '../components/Footer.astro';
const {
content: {
title = 'Use Case',
description = 'Explore how Gitea Mirror helps engineering teams stay resilient.',
canonical = 'https://gitea-mirror.com/use-cases',
}
} = Astro.props;
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>{title} · Gitea Mirror</title>
<meta name="description" content={description} />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="canonical" href={canonical} />
<script is:inline>
const theme = localStorage.getItem('theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.classList.toggle('dark', theme === 'dark');
</script>
</head>
<body class="min-h-screen bg-background text-foreground antialiased">
<Header client:load />
<main class="pt-24 pb-20">
<article class="use-case-content mx-auto max-w-3xl px-4 sm:px-6 lg:px-8">
<slot />
</article>
</main>
<Footer />
</body>
</html>
<style is:global>
.use-case-content {
display: flex;
flex-direction: column;
gap: 2rem;
padding-top: 3rem;
padding-bottom: 3rem;
font-size: 1rem;
line-height: 1.75;
}
.use-case-content > :is(h1, h2, h3) {
font-weight: 700;
color: var(--foreground);
letter-spacing: -0.01em;
}
.use-case-content h1 {
font-size: clamp(2rem, 3vw + 1rem, 2.75rem);
}
.use-case-content h2 {
font-size: clamp(1.5rem, 2.5vw + 0.75rem, 2.125rem);
margin-top: 1rem;
}
.use-case-content h3 {
font-size: clamp(1.25rem, 1.5vw + 0.75rem, 1.5rem);
}
.use-case-content p {
margin: 0;
color: var(--muted-foreground);
}
.use-case-content ul,
.use-case-content ol {
padding-left: 1.5rem;
display: grid;
gap: 0.5rem;
color: var(--muted-foreground);
}
.use-case-content li::marker {
color: var(--primary);
}
.use-case-content pre {
background-color: color-mix(in srgb, var(--muted) 70%, transparent);
border-radius: 1rem;
padding: 1rem 1.25rem;
font-family: 'Fira Code', 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;
font-size: 0.95rem;
overflow-x: auto;
}
.use-case-content code:not(pre code) {
background-color: color-mix(in srgb, var(--muted) 70%, transparent);
color: var(--foreground);
padding: 0.1rem 0.35rem;
border-radius: 0.4rem;
font-size: 0.92em;
}
.use-case-content a {
color: color-mix(in srgb, var(--primary) 85%, var(--accent));
font-weight: 600;
text-decoration: underline;
text-decoration-thickness: 2px;
text-decoration-color: color-mix(in srgb, var(--primary) 50%, transparent);
}
.use-case-content a:hover {
text-decoration-color: var(--primary);
}
</style>

35
www/src/lib/use-cases.ts Normal file
View File

@@ -0,0 +1,35 @@
export interface UseCase {
slug: string;
title: string;
summary: string;
painPoint: string;
outcome: string;
tags: string[];
}
export const useCases: UseCase[] = [
{
slug: 'backup-github-repositories',
title: 'Backup GitHub Repositories',
summary: 'Continuously mirror GitHub repositories into self-hosted Gitea so your side projects stay safe even when GitHub hiccups.',
painPoint: 'Homelabbers rely on GitHub availability but want local backups that preserve history, metadata, and LFS assets.',
outcome: 'Automated syncs capture full repository history, metadata, and file storage so you always have an up-to-date local copy.',
tags: ['Redundancy', 'Continuous Sync', 'Homelab'],
},
{
slug: 'deploy-with-helm-chart',
title: 'Deploy with Helm Chart',
summary: 'Install the project on Kubernetes in a few commands using the maintained Helm chart to keep your backup mirror humming.',
painPoint: 'Self-hosters want reproducible Git backups without hand-rolling manifests for every cluster or upgrade.',
outcome: 'Versioned Helm values capture backup config, making redeploys and upgrades fast, scriptable, and low-risk.',
tags: ['Kubernetes', 'Helm', 'Homelab'],
},
{
slug: 'proxmox-lxc-homelab',
title: 'Spin Up on Proxmox LXC',
summary: 'Run the one-liner Proxmox VE script to launch gitea-mirror inside a tuned LXC container for your lab backups.',
painPoint: 'Proxmox homelabbers want a repeatable Git backup without manually wiring containers, volumes, and services.',
outcome: 'The community script provisions the container, installs Bun, and wires persistence so mirroring works minutes after boot.',
tags: ['Proxmox', 'Automation', 'Homelab'],
},
];

View File

@@ -4,6 +4,7 @@ import { Header } from '../components/Header';
import { Hero } from '../components/Hero'; import { Hero } from '../components/Hero';
import ShaderBackground from '../components/ShaderBackground.astro'; import ShaderBackground from '../components/ShaderBackground.astro';
import Features from '../components/Features.astro'; import Features from '../components/Features.astro';
import UseCases from '../components/UseCases.astro';
import Screenshots from '../components/Screenshots.astro'; import Screenshots from '../components/Screenshots.astro';
import { Installation } from '../components/Installation'; import { Installation } from '../components/Installation';
import { CTA } from '../components/CTA'; import { CTA } from '../components/CTA';
@@ -123,6 +124,7 @@ const structuredData = {
<Hero client:load /> <Hero client:load />
</div> </div>
<Features /> <Features />
<UseCases />
<Screenshots /> <Screenshots />
<Installation client:load /> <Installation client:load />
<CTA client:load /> <CTA client:load />

View File

@@ -0,0 +1,92 @@
---
layout: ../../layouts/UseCaseLayout.astro
title: "Backup GitHub Repositories with Gitea Mirror"
description: "Run a homelab-friendly playbook to mirror GitHub into self-hosted Gitea with automated schedules, health checks, and restore drills."
canonical: "https://gitea-mirror.com/use-cases/backup-github-repositories/"
---
## Why homelabbers care
GitHub is great—right up until an outage, SSO change, or account lockout strands your projects. Gitea Mirror keeps a self-hosted copy of everything (history, metadata, LFS) so you can keep working locally. This playbook walks through the minimal Docker setup the project ships with and shows how to prove your backups actually work.
## Requirements
- Docker Engine and Compose on the host that will run the mirror
- A GitHub personal access token with `repo`, `read:org`, and `admin:org` if you mirror orgs
- A self-hosted Gitea instance (can be on the same box) and admin or org owner credentials
- Open ports 4321 (web UI) and 3000 (default Gitea) inside your network
## Step-by-step
### 1. Clone the repo and start the stack
```bash
git clone https://github.com/RayLabsHQ/gitea-mirror.git
cd gitea-mirror
docker compose -f docker-compose.alt.yml up -d
```
The `alt` compose file ships with sane defaults for a single-node backup mirror. It stores data in `./data`. If you need a different location, set `DATA_DIR=/path` in `.env` before boot.
Verify the containers:
```bash
docker compose -f docker-compose.alt.yml ps
docker compose -f docker-compose.alt.yml logs -f gitea-mirror
```
Wait for "Server started" before moving on.
### 2. Generate tokens and connect GitHub
1. Create a GitHub personal access token (classic) with at least `repo`, `read:org`, and `admin:org` if you want organization mirrors.
2. Log in to Gitea and create an access token for an admin/owner account with `write:repository`.
3. Visit `http://<host>:4321` and sign up—the first user becomes admin.
4. Complete the setup wizard:
- Paste the GitHub PAT and Gitea URL/token.
- Choose which GitHub owners (user/org) to track.
- Leave sync interval at the default 1 hour to start.
### 3. Stage your first backup job
On the dashboard:
1. Click **Sync Now** for a small test repository.
2. Open Gitea and confirm the mirror appears with the right owner/org.
3. Enable **Mirror metadata** and **Mirror LFS** if you rely on issues, wikis, or large assets.
For broader coverage, switch the organization strategy to **Preserve structure** so Gitea mirrors your GitHub org layout automatically.
### 4. Schedule a recurring sync window
Under **Settings → Mirror Options**:
- Set the global interval (for example 30 minutes) to keep backups fresh.
- If you only need nightly backups, add a schedule window such as `02:00-02:30`.
- Enable **Auto-discovery** so new repositories are picked up whenever they appear.
### 5. Prove the backup works
Treat the mirror like any other DR asset:
1. Temporarily block outbound GitHub access on your machine.
2. Clone from Gitea instead: `git clone http://<gitea-host>/<owner>/<repo>.git`.
3. Confirm commit history, tags, releases, and issues exist.
4. Remove the block and document the restore steps in your homelab wiki.
## Health checks & monitoring
- The container exposes `/api/health`; add it to Uptime Kuma, Healthchecks.io, or Prometheus.
- Mirror failures surface in the activity log; consider exporting them through the `/api/events` endpoint.
- Disk usage lives under `Settings → Storage`; make sure the host volume has headroom for LFS blobs.
## Hardening tips
- Put the stack behind a reverse proxy (Traefik, Caddy, Nginx) and enable TLS.
- Rotate both GitHub and Gitea tokens quarterly; the UI will flag expired credentials.
- Snapshot the `data/` volume (ZFS/BTRFS) or back it up with `restic` so the mirror survives host failure.
## Next steps
- Promote the mirror to read-only users who do not need GitHub access.
- Layer on the [Helm](../deploy-with-helm-chart) or [Proxmox LXC](../proxmox-lxc-homelab) playbooks when you outgrow the single-node setup.

View File

@@ -0,0 +1,106 @@
---
layout: ../../layouts/UseCaseLayout.astro
title: "Deploy Gitea Mirror with the Helm Chart"
description: "Install the Gitea Mirror backup service on Kubernetes with the official Helm chart, including secrets, persistence, and upgrade workflow."
canonical: "https://gitea-mirror.com/use-cases/deploy-with-helm-chart/"
---
## Why ship it to Kubernetes
If your homelab already runs a cluster (k3s, Talos, MicroK8s), Helm is the fastest way to keep Gitea Mirror close to the rest of your self-hosted stack. The chart in `helm/gitea-mirror` bundles the deployment, service, ingress, and persistence so you can version your backup mirror just like any other release.
## Requirements
- Kubernetes 1.23+ with storage (Rook, Longhorn, local-path, etc.)
- Helm 3.8+
- GitHub PAT and Gitea API token ready (same scopes as the Docker playbook)
- Namespace with outbound access to GitHub and your Gitea host
## Step-by-step
### 1. Prepare a values file
Create `values-gitea-mirror.yaml` with the credentials you want the chart to render. Only `github` and `gitea` fields are required for a basic backup deployment.
```yaml
gitea-mirror:
github:
username: "your-gh-user"
token: "ghp_..."
gitea:
url: "https://git.lab.local"
token: "gitea_..."
mirror:
interval: "30m"
owners:
- "your-gh-user"
persistence:
enabled: true
size: 5Gi
service:
type: ClusterIP
ingress:
enabled: true
className: "traefik"
hosts:
- host: "mirror.lab.local"
paths:
- path: "/"
pathType: Prefix
tls:
- hosts: ["mirror.lab.local"]
secretName: "mirror-tls"
```
### 2. Install into a namespace
```bash
kubectl create namespace gitea-mirror
helm upgrade --install gitea-mirror ./helm/gitea-mirror \
--namespace gitea-mirror \
--values values-gitea-mirror.yaml
```
Helm renders a `Deployment`, `Service`, optional `Ingress`, and PVC if persistence is enabled. The pod mounts storage at `/app/data` for the SQLite DB and cached repositories.
### 3. Verify the release
```bash
kubectl -n gitea-mirror get pods,svc,pvc
kubectl -n gitea-mirror logs deploy/gitea-mirror --tail=100
```
Watch for `Server started` in the logs. Once ready, browse to the ingress host (or userland port-forward with `kubectl port-forward svc/gitea-mirror 4321:8080`). Complete the first-run wizard just like the Docker playbook.
### 4. Keep it updated
- Pull chart updates when you bump the repo: `git pull` then re-run the `helm upgrade` command.
- Override the container image tag with `--set image.tag=v3.7.2` if you need to pin.
- Use Helm rollbacks if a release misbehaves: `helm rollback gitea-mirror <REVISION> -n gitea-mirror`.
## Observability
- Attach the `/api/health` endpoint to your clusters probing (Kubernetes probes are already configured by the chart).
- Expose the metrics endpoint via a `ServiceMonitor` if you run Prometheus; add `extraAnnotations` to make it discoverable.
- Watch PVC growth with `kubectl df-pv` or your storage dashboard to ensure LFS blobs do not exhaust the volume.
## Disaster-recovery drill
1. Scale the deployment down: `kubectl -n gitea-mirror scale deploy gitea-mirror --replicas=0`.
2. Snapshot the PVC (CSI snapshots or Velero).
3. Restore into a test namespace and scale the deployment back up.
4. Confirm you can log in and the mirrored repositories are intact.
## Cleanup
```bash
helm uninstall gitea-mirror -n gitea-mirror
kubectl delete namespace gitea-mirror
```
Remove the PVC manually if you want a clean slate: `kubectl delete pvc gitea-mirror-storage -n gitea-mirror`.
Ready to run on bare metal instead? Head over to the [Proxmox LXC playbook](../proxmox-lxc-homelab).

View File

@@ -0,0 +1,61 @@
---
layout: ../../layouts/UseCaseLayout.astro
title: "Gitea Mirror Use Cases"
description: "Discover the top workflows teams unlock with automated GitHub to Gitea mirroring."
canonical: "https://gitea-mirror.com/use-cases/"
---
import { useCases } from '@/lib/use-cases';
import { ArrowRight } from 'lucide-react';
<div class="mx-auto max-w-3xl px-4 text-center">
<span class="inline-flex items-center rounded-full border px-3 py-1 text-xs font-semibold uppercase tracking-widest text-muted-foreground">
Use Case Library
</span>
<h1 class="mt-6 text-3xl font-bold sm:text-4xl md:text-5xl">
Operational playbooks for backup-ready engineering teams
</h1>
<p class="mt-4 text-base sm:text-lg text-muted-foreground">
Each guide shows how to configure Gitea Mirror, schedule mirrors, and validate results so your team can ship with confidence.
</p>
</div>
<div class="mx-auto mt-16 grid max-w-6xl gap-6 px-4 sm:gap-8">
{useCases.map((useCase) => (
<article class="group relative overflow-hidden rounded-3xl border bg-background/80 p-8 shadow-sm transition-all duration-300 hover:-translate-y-1 hover:shadow-lg">
<div class="absolute inset-0 rounded-3xl bg-gradient-to-br from-primary/5 via-accent/5 to-transparent opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
<div class="relative">
<div class="flex flex-wrap items-center gap-3 text-xs font-medium uppercase tracking-wider text-muted-foreground">
{useCase.tags.map((tag) => (
<span class="inline-flex items-center rounded-full border border-muted px-3 py-1">
{tag}
</span>
))}
</div>
<h2 class="mt-6 text-2xl font-semibold sm:text-3xl">
{useCase.title}
</h2>
<p class="mt-4 text-sm sm:text-base text-muted-foreground">
{useCase.summary}
</p>
<div class="mt-6 grid gap-4 text-sm sm:text-base text-muted-foreground">
<div>
<h3 class="text-sm font-semibold uppercase tracking-wide text-foreground/80">Pain Point</h3>
<p>{useCase.painPoint}</p>
</div>
<div>
<h3 class="text-sm font-semibold uppercase tracking-wide text-foreground/80">Outcome</h3>
<p>{useCase.outcome}</p>
</div>
</div>
<a
href={`/use-cases/${useCase.slug}/`}
class="mt-8 inline-flex items-center gap-2 text-sm font-semibold text-primary transition-colors hover:text-primary/80"
>
Read the playbook
<ArrowRight class="h-4 w-4 transition-transform group-hover:translate-x-1" />
</a>
</div>
</article>
))}
</div>

View File

@@ -0,0 +1,80 @@
---
layout: ../../layouts/UseCaseLayout.astro
title: "Run Gitea Mirror inside a Proxmox LXC"
description: "Provision the community-maintained Proxmox VE LXC container for Gitea Mirror and wire it into your homelab backup workflow."
canonical: "https://gitea-mirror.com/use-cases/proxmox-lxc-homelab/"
---
## Why run it on Proxmox
When most of your homelab lives in Proxmox VE, the community LXC script is the fastest path from zero to a managed Gitea Mirror node. It handles Bun, systemd, persistent storage, and future upgrades so you can focus on keeping Git backups fresh.
## Requirements
- Proxmox VE host with the [community-scripts repository](https://community-scripts.github.io/ProxmoxVE/) enabled
- Storage pool with ~6GB free (default script allocation) and an available bridge (usually `vmbr0`)
- GitHub PAT and Gitea token scoped for mirroring
- DNS or IP address for the container on your LAN
## Step-by-step
### 1. Launch the installer
SSH into the Proxmox host and run:
```bash
bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/gitea-mirror.sh)"
```
The helper script prompts for:
- **Node**: the Proxmox host that will own the container
- **Storage**: local-lvm, ZFS dataset, etc.
- **Network bridge**: e.g. `vmbr0`
- **IP assignment**: DHCP or static (consider static for a backup appliance)
- **Container size**: defaults to 2 vCPU, 2GiB RAM, 6GiB disk—bump RAM if you mirror large orgs
Accept the defaults or adjust as needed. The script downloads the release, installs Bun, seeds systemd, and prints the access URL on completion.
### 2. First login and setup
1. Browse to `http://<container-ip>:4321`.
2. Create the admin account (first user = admin).
3. Enter your GitHub PAT and Gitea API token in the onboarding wizard.
4. Select the GitHub owners you want mirrored and enable auto-discovery for new repos.
### 3. Validate persistence and services
Inside the container (`pct enter <CTID>`):
```bash
systemctl status gitea-mirror
ls /opt/gitea-mirror/data
```
You should see `gitea-mirror.db` and a `repos/` directory. Data lives under `/opt/gitea-mirror/data`; back it up or snapshot the underlying storage pool regularly.
### 4. Expose the service (optional)
- Add a Proxmox firewall rule or reverse proxy entry (Traefik/Caddy/HAProxy) if you want TLS.
- Create a DNS record (`mirror.lab.local`) pointed at the container for easier access.
### 5. Upgrades & maintenance
- Re-run the installer script; it detects existing installs, backs up `/opt/gitea-mirror/data`, downloads the latest release, and restarts the service.
- Watch for warnings about upgrades from v2 → v3 wiping config—take a snapshot first if you still run v2 artifacts.
- Check logs with `journalctl -u gitea-mirror -n 200` or `journalctl -u gitea-mirror -f` for live tailing.
## Disaster-recovery drill
1. In Proxmox, snapshot the container (or use ZFS/BTRFS snapshots on the storage pool).
2. Stop GitHub access temporarily and clone from the Gitea mirror to confirm the backup works.
3. Restore the snapshot on a different node to ensure the service boots cleanly with the preserved data volume.
## Troubleshooting
- **Port 4321 already used**: change `PORT` in `/opt/gitea-mirror.env` and restart with `systemctl restart gitea-mirror`.
- **Disk full**: extend the container disk in Proxmox, then run `pct resize <CTID> rootfs +5G`.
- **Token expired**: log in to the UI → Settings to update GitHub/Gitea credentials.
Looking for a cluster-native deployment? Try the [Helm playbook](../deploy-with-helm-chart).