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 { DataFlowDiagram } from "@/components/ui/data-flow-diagram" 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.lxcNfsServer.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/lxc-nfs-server", }, } } type NetworkRow = { mode: string; value: string; when?: string; whenRich?: string } type OptionRow = { option: string; effect?: string; effectRich?: string } type StringItem = string type RelatedItem = { href: string; label: string; tail?: string; tailRich?: string } export default async function LxcNfsServerPage({ params, }: { params: Promise<{ locale: string }> }) { const { locale } = await params setRequestLocale(locale) const t = await getTranslations({ locale, namespace: "docs.storageShare.lxcNfsServer" }) const messages = (await getMessages({ locale })) as unknown as { docs: { storageShare: { lxcNfsServer: { network: { rows: NetworkRow[] } options: { rows: OptionRow[] } troubleshoot: { aptItems: StringItem[]; ownItems: StringItem[] } related: { items: RelatedItem[] } } } } } const networkRows = messages.docs.storageShare.lxcNfsServer.network.rows const optionRows = messages.docs.storageShare.lxcNfsServer.options.rows const aptItems = messages.docs.storageShare.lxcNfsServer.troubleshoot.aptItems const ownItems = messages.docs.storageShare.lxcNfsServer.troubleshoot.ownItems const relatedItems = messages.docs.storageShare.lxcNfsServer.related.items const code = (chunks: React.ReactNode) => {chunks} const strong = (chunks: React.ReactNode) => {chunks} const em = (chunks: React.ReactNode) => {chunks} return (
{t.rich("privReq.body", { code, strong })}

{t("what.heading")}

{t.rich("what.body", { em, strong })}

(rw,sync,no_subtree_check,no_root_squash)`} />

{t("shared.heading")}

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

{t.rich("shared.gidBody", { strong, code })} {t.rich("shared.remapBody", { code, strong })}

{t("defaults.heading")}

{t.rich("defaults.warnBody", { code, strong })}

{t("opening.heading")}

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

{t("opening.imageAlt")}

{t("howRuns.heading")}

{`┌─────────────────────────────────────────────┐
│  PHASE 1 — Pick CT, folder, network, opts   │
│  (nothing touched yet)                      │
└──────────────────┬──────────────────────────┘
                   ▼
      Privileged-CT gate (share-common.func)
      ├─ pct list — pick CT
      ├─ Auto-start if stopped
      └─ Aborts if "unprivileged: 1" in CT config
                   │
                   ▼
      Folder selection (2 modes)
      ├─ Auto: choose from existing folders
      │   inside /mnt of the CT
      └─ Manual: enter any absolute path
         (must already exist inside the CT)
                   │
                   ▼
      Network ACL (3 modes)
      ├─ 1. Local network (192.168.0.0/16)
      ├─ 2. Custom subnet (e.g. 192.168.10.0/24)
      └─ 3. Single host IP
                   │
                   ▼
      Export options (3 modes)
      ├─ 1. Read-write — rw,sync,no_subtree_check,
      │                  no_root_squash  (DEFAULT)
      ├─ 2. Read-only  — ro,sync,no_subtree_check,
      │                  no_root_squash
      └─ 3. Custom     — type your own option string
                   │
   ┌──────── Cancel   OR   Confirm ────┐
   ▼                                   ▼
Exit, nothing        ┌─────────────────┴─────────────────┐
was changed          │  PHASE 2 — Install + configure     │
                     └─────────────────┬─────────────────┘
                                       ▼
                       Install NFS server (in CT)
                       └─ pct exec apt-get install -y \\
                              nfs-kernel-server
                              nfs-common rpcbind
                          + systemctl enable --now both
                          (skipped if already installed)
                                       ▼
                       setup_universal_sharedfiles_group
                       └─ groupadd -g 101000 sharedfiles
                          (or groupmod if exists at wrong GID)
                          For each regular user (UID >= 1000):
                            ├─ usermod -a -G sharedfiles 
                            └─ useradd -u  \\
                                       -g sharedfiles \\
                                       remap_
                          Same for common UIDs (33, 1000-1002)
                                       ▼
                       Apply ownership + SGID on the folder
                       └─ chown root:sharedfiles 
                          chmod 2775 
                            (sticky group: new files inherit
                             the sharedfiles group)
                                       ▼
                       Update /etc/exports
                       └─ If existing entry for the folder:
                              ask "update?", remove + replace.
                          Else:
                              append the new line.
                                       ▼
                       systemctl restart rpcbind \\
                                         nfs-kernel-server
                       exportfs -ra
                                       ▼
                       Print connection details:
                       • Server IP (CT hostname -I)
                       • Export path
                       • Mount options chosen
                       • Network ACL
                       • Mount examples (auto / v4 / v3)`}
      

{t("network.heading")}

{t.rich("network.intro", { code })}

{networkRows.map((row, idx) => ( ))}
{t("network.headerMode")} {t("network.headerValue")} {t("network.headerWhen")}
{row.mode} {row.value} {row.whenRich ? t.rich(`network.rows.${idx}.whenRich`, { code }) : row.when}

{t("options.heading")}

{optionRows.map((row, idx) => ( ))}
{t("options.headerOption")} {t("options.headerEffect")}
{row.option} {row.effectRich ? t.rich(`options.rows.${idx}.effectRich`, { code, strong }) : row.effect}

{t("manual.heading")}

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

= 1000 && $3 < 65534 {print $1}' /etc/passwd); do usermod -a -G sharedfiles "$u" done # 3. set ownership + SGID on the folder mkdir -p /mnt/data chown root:sharedfiles /mnt/data chmod 2775 /mnt/data # SGID: new files inherit group # 4. add the export line echo "/mnt/data 192.168.0.0/16(rw,sync,no_subtree_check,no_root_squash)" \\ >> /etc/exports # 5. apply systemctl restart rpcbind nfs-kernel-server exportfs -ra # verify exportfs -v showmount -e localhost`} />

{t("view.heading")}

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

{t("delete.heading")}

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

{t("status.heading")}

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

{t("uninstall.heading")}

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

{t.rich("uninstall.warnBody", { em, code })}

{t("troubleshoot.heading")}

{t.rich("troubleshoot.privBody", { code })} {t("troubleshoot.aptIntro")}
    {aptItems.map((_, idx) => (
  • {t.rich(`troubleshoot.aptItems.${idx}`, { code })}
  • ))}
{t("troubleshoot.aptOutro")}
{t.rich("troubleshoot.aclBody", { code })} {t("troubleshoot.ownIntro")}
    {ownItems.map((_, idx) => (
  • {t.rich(`troubleshoot.ownItems.${idx}`, { code })}
  • ))}
{t.rich("troubleshoot.noShowBody", { code })}

{t("related.heading")}

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