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.hostLocalDisk.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-local-disk", }, } } type CompareRow = { label: string; dir?: string; zfs?: string; dirRich?: string; zfsRich?: string } type StringItem = string type PresetRow = { preset: string; content: string; use?: string; useRich?: string } type RelatedItem = { href: string; label: string; tail?: string } export default async function HostLocalDiskPage({ params, }: { params: Promise<{ locale: string }> }) { const { locale } = await params setRequestLocale(locale) const t = await getTranslations({ locale, namespace: "docs.storageShare.hostLocalDisk" }) const messages = (await getMessages({ locale })) as unknown as { docs: { storageShare: { hostLocalDisk: { compare: { rows: CompareRow[] } format: { items: StringItem[] } reuse: { items: StringItem[] } presets: { rows: PresetRow[] } troubleshoot: { noDisksItems: StringItem[] } related: { items: RelatedItem[] } } } } } const compareRows = messages.docs.storageShare.hostLocalDisk.compare.rows const formatItems = messages.docs.storageShare.hostLocalDisk.format.items const reuseItems = messages.docs.storageShare.hostLocalDisk.reuse.items const presetRows = messages.docs.storageShare.hostLocalDisk.presets.rows const noDisksItems = messages.docs.storageShare.hostLocalDisk.troubleshoot.noDisksItems const relatedItems = messages.docs.storageShare.hostLocalDisk.related.items const code = (chunks: React.ReactNode) => {chunks} const strong = (chunks: React.ReactNode) => {chunks} const em = (chunks: React.ReactNode) => {chunks} return (
{t.rich("intro.body", { em, strong })} {t.rich("destructive.body", { em, strong, code })}

{t("compare.heading")}

{compareRows.map((row, idx) => ( ))}
  {t("compare.headerDir")} {t("compare.headerZfs")}
{row.label} {row.dirRich ? t.rich(`compare.rows.${idx}.dirRich`, { code }) : row.dir} {row.zfsRich ? t.rich(`compare.rows.${idx}.zfsRich`, { code }) : row.zfs}

{t("opening.heading")}

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

{t("opening.imageAlt")}

{t("howRuns.heading")}

{t("howRuns.body")}

{`┌─────────────────────────────────────────────┐
│  PHASE 1 — Detect, inspect, plan            │
│  (nothing touched yet)                      │
└──────────────────┬──────────────────────────┘
                   ▼
      Dependency check
      └─ parted / mkfs.ext4 / mkfs.xfs / blkid /
         lsblk / sgdisk present?
         If any missing → apt-get install
             parted e2fsprogs util-linux
             xfsprogs gdisk btrfs-progs
                   │
                   ▼
      Disk detection (lsblk -dn -e 7,11)
                   │
                   ▼
      Safety filter
      ├─ Hidden: type != "disk" (skip partitions)
      ├─ Hidden: read-only (ro=1)
      ├─ Hidden: /dev/zd* (ZFS volumes, not disks)
      ├─ Hidden: used by host storage
      │         (root pool, mounted paths, ZFS/LVM)
      └─ Hidden: referenced by any VM/LXC config
                   │
                   ▼
      User selects a disk
      (menu shows disk path + size + model)
                   │
                   ▼
      Disk inspection (blkid / lsblk)
      ├─ Has data → offer 2 actions:
      │      ├─ Format disk (ERASE all)
      │      └─ Use existing filesystem
      └─ Empty     → only "Format disk"
                   │
                   ▼
      If "Format" was chosen:
      Filesystem picker
      ├─ ext4   → dir storage (recommended general use)
      ├─ xfs    → dir storage (large files / VMs)
      ├─ btrfs  → dir storage (snapshots / compression)
      └─ zfs    → ZFS POOL storage (different path)
                   │
                   ▼
      Storage ID  (default: "disk-")
      Mount path  (default: "/mnt/")
      Content types  (4 presets + custom):
      ├─ 1. VM Storage     → images,backup
      ├─ 2. Standard NAS   → backup,iso,vztmpl
      ├─ 3. All types      → images,backup,iso,vztmpl,snippets
      └─ 4. Custom         → free CSV input
                   │
   ┌──────── Cancel   OR   Confirm ────┐
   ▼                                   ▼
Exit, nothing        ┌─────────────────┴─────────────────┐
was changed          │  PHASE 2 — Execute                 │
                     └─────────────────┬─────────────────┘
                                       ▼
                       FORMAT PATH (destructive):
                       ├─ Final "ERASE confirmation" dialog
                       │   → Cancel exits here
                       ├─ wipefs + sgdisk --zap-all
                       ├─ parted/sgdisk: create partition
                       ├─ ZFS pre-flight:
                       │   • zpool command present?
                       │   • pool name not already in use?
                       ├─ mkfs. / zpool create
                       │   (mkfs.ext4 / xfs / btrfs / zfs pool)
                       ├─ Non-ZFS: mount -t  + UUID
                       │     entry in /etc/fstab with
                       │     defaults,nofail
                       └─ ZFS: zpool manages its own mount
                                       ▼
                       REUSE PATH (existing fs):
                       ├─ blkid detects filesystem type
                       ├─ mkdir mount point
                       ├─ mount  
                       └─ UUID entry in /etc/fstab
                                       ▼
                       Register in Proxmox:
                       ├─ filesystem == zfs →
                       │    pvesm add zfspool  \\
                       │         --pool  \\
                       │         --content 
                       └─ otherwise →
                            pvesm add dir  \\
                                --path  \\
                                --content 
                                       ▼
                       Summary + "visible in Datacenter →
                       Storage" confirmation`}
      

{t("format.heading")}

{t("format.intro")}

    {formatItems.map((_, idx) => (
  1. {t.rich(`format.items.${idx}`, { code, strong })}
  2. ))}
{t.rich("format.tipBody", { code })}

{t("reuse.heading")}

{t("reuse.intro")}

    {reuseItems.map((_, idx) => (
  1. {t.rich(`reuse.items.${idx}`, { code, strong })}
  2. ))}
{t.rich("reuse.warnBody", { em, code })}

{t("presets.heading")}

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

{presetRows.map((row, idx) => ( ))}
{t("presets.headerPreset")} {t("presets.headerContent")} {t("presets.headerUse")}
{row.preset} {row.content} {row.useRich ? t.rich(`presets.rows.${idx}.useRich`, { code }) : row.use}
{t.rich("presets.zfsBody", { strong, code })}

{t("manual.heading")}

{t("manual.extIntro")}

> /etc/fstab mount /mnt/mydisk # 5. register in Proxmox pvesm add dir mydisk \\ --path /mnt/mydisk \\ --content images,backup`} />

{t("manual.zfsIntro")}

{t("view.heading")}

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

{t("remove.heading")}

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

{t("remove.warnBody")}

{t("list.heading")}

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

{t("troubleshoot.heading")}

{t("troubleshoot.noDisksIntro")}
    {noDisksItems.map((_, idx) => (
  • {t(`troubleshoot.noDisksItems.${idx}`)}
  • ))}
{t.rich("troubleshoot.noDisksOutro", { em, code })}
{t.rich("troubleshoot.mountedBody", { code })} {t.rich("troubleshoot.zpoolBody", { code })} {t.rich("troubleshoot.inactiveBody", { code })}

{t("related.heading")}

    {relatedItems.map((item) => (
  • {item.label} {item.tail}
  • ))}
) }