import type { Metadata } from "next" import { getTranslations, getMessages, setRequestLocale } from "next-intl/server" import { Link } from "@/i18n/navigation" import Image from "next/image" import { DocHeader } from "@/components/ui/doc-header" import { Callout } from "@/components/ui/callout" import CopyableCode from "@/components/CopyableCode" export async function generateMetadata({ params, }: { params: Promise<{ locale: string }> }): Promise { const { locale } = await params const t = await getTranslations({ locale, namespace: "docs.storageShare.hostNfs.meta" }) return { title: t("title"), description: t("description"), openGraph: { title: t("ogTitle"), description: t("ogDescription"), type: "article", url: "https://macrimi.github.io/ProxMenux/docs/storage-share/host-nfs", }, } } type StringItem = string type ModesRow = { method: string; mount?: string; mountRich?: string; ui: string; useCase?: string; useCaseRich?: string } type ContentRow = { type: string; allows?: string; allowsRich?: string } type RelatedItem = { href: string; label: string; tail?: string; tailRich?: string } export default async function HostNfsPage({ params, }: { params: Promise<{ locale: string }> }) { const { locale } = await params setRequestLocale(locale) const t = await getTranslations({ locale, namespace: "docs.storageShare.hostNfs" }) const messages = (await getMessages({ locale })) as unknown as { docs: { storageShare: { hostNfs: { modes: { rows: ModesRow[] } pvesmBranch: { items: StringItem[]; rows: ContentRow[] } fstabBranch: { items: StringItem[]; applies: StringItem[] } related: { items: RelatedItem[] } } } } } const modesRows = messages.docs.storageShare.hostNfs.modes.rows const pvesmItems = messages.docs.storageShare.hostNfs.pvesmBranch.items const contentRows = messages.docs.storageShare.hostNfs.pvesmBranch.rows const fstabItems = messages.docs.storageShare.hostNfs.fstabBranch.items const fstabAppliesItems = messages.docs.storageShare.hostNfs.fstabBranch.applies const relatedItems = messages.docs.storageShare.hostNfs.related.items const code = (chunks: React.ReactNode) => {chunks} const strong = (chunks: React.ReactNode) => {chunks} const em = (chunks: React.ReactNode) => {chunks} const mountLink = (chunks: React.ReactNode) => ( {chunks} ) return (
{t.rich("intro.body", { code, strong })}

{t("opening.heading")}

{t.rich("opening.body", { strong })}

{t("opening.imageAlt")}

{t("howRuns.heading")}

{t.rich("howRuns.body", { code })}

{`┌─────────────────────────────────────────────┐
│  PHASE 1 — Discover, validate, choose       │
│  (nothing touched yet)                      │
└──────────────────┬──────────────────────────┘
                   ▼
      Dependency check
      └─ nfs-common present? (showmount)
         If missing → apt-get install nfs-common
                   │
                   ▼
      Server selection
      ├─ Auto-discover  (nmap -p 2049 on /24)
      └─ Manual         (type IP or hostname)
                   │
                   ▼
      Reachability + showmount validation
                   │
                   ▼
      Export selection
                   │
                   ▼
      ╔═════════════════════════════════════╗
      ║   MOUNT METHOD PICKER  (checklist)  ║
      ║   [ ] As Proxmox storage  (pvesm)   ║
      ║   [ ] As host fstab mount only      ║
      ║   (mark one or both — re-prompts    ║
      ║    if you press OK without marks)   ║
      ╚════════════════╤════════════════════╝
                       │
        ┌──────────────┴──────────────┐
        ▼                             ▼
   pvesm branch                  fstab branch
   ├─ storage ID                 ├─ mount path
   ├─ content types              └─ mount options
   ▼                             ▼
   ┌─────────────────────────────────────────┐
   │  PHASE 2 — Apply (only marked methods)  │
   └──────────────────┬──────────────────────┘
                      ▼
   pvesm add nfs  ...    +  mkdir -p 
   (auto-mount at            mount -t nfs ...
    /mnt/pve/)           append /etc/fstab
                             systemctl daemon-reload
                             chmod 1777 + setfacl
                             (best-effort, NFS server-side)
                      ▼
              Summary printed`}
      

{t("modes.heading")}

{t("modes.intro")}

{modesRows.map((row, idx) => ( ))}
{t("modes.headerMethod")} {t("modes.headerMount")} {t("modes.headerUi")} {t("modes.headerUseCase")}
{t.rich(`modes.rows.${idx}.method`, { strong })} {row.mountRich ? t.rich(`modes.rows.${idx}.mountRich`, { code }) : row.mount} {row.ui} {row.useCaseRich ? t.rich(`modes.rows.${idx}.useCaseRich`, { em }) : row.useCase}
{t.rich("modes.bothBody", { code })}

{t("pvesmBranch.heading")}

{t.rich("pvesmBranch.intro", { em })}

    {pvesmItems.map((_, idx) => (
  1. {t.rich(`pvesmBranch.items.${idx}`, { strong, code })}
  2. ))}
{contentRows.map((row, idx) => ( ))}
{t("pvesmBranch.headerType")} {t("pvesmBranch.headerAllows")}
{row.type} {row.allowsRich ? t.rich(`pvesmBranch.rows.${idx}.allowsRich`, { em, code }) : row.allows}
{t.rich("pvesmBranch.warnBody", { code })}

{t.rich("pvesmBranch.result", { code })}

{t("fstabBranch.heading")}

{t.rich("fstabBranch.intro", { em, code })}

    {fstabItems.map((_, idx) => (
  1. {t.rich(`fstabBranch.items.${idx}`, { strong, em, code })}
  2. ))}

{t("fstabBranch.appliesIntro")}

    {fstabAppliesItems.map((_, idx) => (
  • {t.rich(`fstabBranch.applies.${idx}`, { code })}
  • ))}
{t.rich("fstabBranch.lxcBody", { code, strong, mountLink })} {t.rich("fstabBranch.noUiBody", { em })}

{t("manual.heading")}

{t("manual.pvesmIntro")}

{t("manual.fstabIntro")}

> /etc/fstab systemctl daemon-reload # Best-effort open perms for LXC bind-mount writes (server permitting) chmod 1777 /mnt/data 2>/dev/null || true setfacl -m o::rwx /mnt/data 2>/dev/null || true # Bind into an unprivileged LXC (host-side perms only — no changes inside CT) pct set -mp0 /mnt/data,mp=/mnt/data,shared=1,backup=0 pct reboot `} />

{t("view.heading")}

{t.rich("view.body", { code, strong })}

{t("remove.heading")}

{t.rich("remove.body", { code, strong })}

{t("remove.warnBody")}

{t("test.heading")}

{t.rich("test.body", { code, em })}

{t("troubleshoot.heading")}

{t.rich("troubleshoot.noServersBody", { code, em })} {t.rich("troubleshoot.portBody", { code })} {t.rich("troubleshoot.showmountBody", { code })} {t.rich("troubleshoot.inactiveBody", { em, code })} {t.rich("troubleshoot.lxcNoWriteBody", { code, strong })} {t.rich("troubleshoot.fstabBootBody", { code })}

{t("related.heading")}

    {relatedItems.map((item, idx) => (
  • {item.label} {item.tailRich ? t.rich(`related.items.${idx}.tailRich`, { code }) : item.tail}
  • ))}
) }