DEVELOPER DOCS

Storage operations,
without guesswork.

Run one Parto binary to inspect the real VM storage path and its health, produce automation-ready JSON, review a dry run, and safely grow mounted XFS.

01 · START HERE

Install one executable

Choose the Parto executable for your operating system and CPU architecture, put it where you keep command-line tools, and run it.

LINUX
chmod +x parto_linux_amd64
sudo mv parto_linux_amd64 /usr/local/bin/parto
parto --version
MACOS · APPLE SILICON
chmod +x parto_darwin_arm64
sudo mv parto_darwin_arm64 /usr/local/bin/parto
parto --version
WINDOWS · POWERSHELL
New-Item -ItemType Directory -Force "$HOME\bin"
Move-Item .\parto_windows_amd64.exe "$HOME\bin\parto.exe"
& "$HOME\bin\parto.exe" --version

Use the arm64 file on ARM64 systems and amd64 on x86-64. On Windows, add $HOME\bin to PATH only if you want to run parto.exe from any directory.

Building from source

make build derives the displayed version from Git. A clean release-tag checkout reports an exact version such as parto v0.3.0; later commits include commit metadata, and uncommitted changes add a dirty suffix.

SOURCE BUILD
make verify
make build
./dist/parto --version
Safe first run

Start with read-only inspection. Then run a size-specific dry run. Remove --dry-run only after both outputs look correct.

RECOMMENDED SEQUENCE
parto inspect --provider openstack [options]
parto grow --provider openstack [options] --dry-run
parto grow --provider openstack [options]

02 · READY CHECK

What Parto needs

Parto connects three parts of the operation: the machine running the command, the selected cloud provider, and the target VM. Have each one ready before the first inspection.

01OPERATOR

Run the Parto binary

A Linux, macOS, or Windows Parto executable matching the CPU architecture, cloud API access, and SSH access directly or through PAS.

02CLOUD

Authenticate to your cloud

OpenStack credentials or an AWS SDK credential chain with permission to inspect instances and volumes, plus the permissions required by the grow path you use.

03TARGET

Reach the target VM

A mounted non-root XFS data filesystem, sudo access, and the required Linux storage utilities. Parto detects Ubuntu/Debian or Amazon Linux after SSH connects.

MANAGED ACCESS

Optional PostgreSQL environments

PostgreSQL is optional. Explicit OpenStack --environment needs it. When PARTO_DATABASE_URL is configured and neither OpenStack selector is supplied, Parto checks registered environments first. Explicit --cloud, local-only OpenStack workflows, and AWS do not need a database.

Select the provider every time

Every inspect and grow command requires either --provider openstack or --provider aws. Parto has no default provider and never guesses from local credentials.

TARGET UTILITIES

Common packages include util-linux, parted, lvm2, and xfsprogs. Partition-backed XFS also needs growpart from cloud-guest-utils on Ubuntu/Debian or cloud-utils-growpart on Amazon Linux. smartmontools is optional for best-effort SMART inspection.

03 · CONNECT

Configure a cloud provider

Select --provider openstack or --provider aws explicitly for every inspect and grow operation. There is no default. The provider controls cloud discovery and volume operations; SSH or PAS still carries out guest storage work.

OpenStack

For inspect and grow, Parto searches the standard clouds.yaml locations automatically after --provider openstack is selected. Use --cloud to select a profile explicitly. Managed environment import and synchronization use an explicit --file path.

OpenStack credential order

Explicit --environment selects PostgreSQL. Explicit --cloud bypasses PostgreSQL. With neither selector, Parto checks a configured database first: one record is selected, zero records continue to local discovery, and multiple records require --environment. Local discovery then checks OS_CLOUD, one unambiguous clouds.yaml profile, and finally OS_*.

Use a local profile directly

parto inspect --provider openstack --cloud example-cloud bypasses automatic database lookup and selects the example-cloud key under clouds:. The same profile can be imported into PostgreSQL later if managed environments are useful.

Automatic configuration discovery

Without either selector, an unset database configuration or a successfully queried database with zero records continues to local discovery. A configured database failure stops instead of being hidden. Local discovery checks OS_CLOUD, selects the only profile in the first discovered clouds.yaml, then falls back to standard OS_*. Multiple local profiles require --cloud.

~/.config/openstack/clouds.yaml
clouds:
  example-cloud:
    auth_type: password
    auth:
      auth_url: https://identity.example.com/v3
      username: example-user
      project_name: example-project
      user_domain_name: Default
      project_domain_name: Default
    region_name: RegionOne
    interface: public
    identity_api_version: 3
    verify: true
~/.config/openstack/secure.yaml
clouds:
  example-cloud:
    auth:
      password: replace-with-your-openstack-password
INSTALL AND VERIFY
chmod 600 ~/.config/openstack/clouds.yaml ~/.config/openstack/secure.yaml
openstack --os-cloud example-cloud token issue
openstack --os-cloud example-cloud server show my-vm
openstack --os-cloud example-cloud server volume list my-vm
Keep TLS verification enabled

For a private CA, add cacert: /path/to/company-ca.pem. Do not solve certificate errors with verify: false.

Parto requests Cinder API microversion 3.42 for online extension of attached volumes. The cloud policy and storage backend must also allow it.

AWS EBS · preview

Parto uses the AWS SDK default credential chain and requires a region. It selects one running EC2 instance by the exact Name tag passed to --vm-name, then uses its public IPv4 address when available. Private-only instances fall back to private IPv4 and require a routed network or PAS. AWS SSM is not included.

AWS PROFILE AND READ-ONLY INSPECTION
export AWS_PROFILE=production
export AWS_REGION=eu-central-1

parto inspect \
  --provider aws \
  --vm-name search-01 \
  --mountpoint /data \
  --ssh-user ubuntu \
  --ssh-key ~/.ssh/id_rsa
AMAZON LINUX · NO OS FLAG
parto inspect \
  --provider aws \
  --vm-name search-amzn-01 \
  --mountpoint /data \
  --ssh-user ec2-user \
  --ssh-key ~/.ssh/parto-amazon-linux.pem
Guest Linux is detected automatically

Parto reads /etc/os-release after SSH connects. There is no OS-selection flag: Ubuntu/Debian and Amazon Linux use the same capability-based XFS workflow. The SSH account remains explicit because login happens before detection; standard Ubuntu images commonly use ubuntu, while Amazon Linux images commonly use ec2-user.

Missing tools stop safely

Parto never installs packages. Preflight reports missing commands and suggests apt-get for Ubuntu/Debian, dnf for Amazon Linux 2023, or yum for Amazon Linux 2 before any storage change begins.

IAM starting point

Inspection needs ec2:DescribeInstances, ec2:DescribeVolumes, and ec2:DescribeVolumesModifications. Existing-volume growth adds ec2:ModifyVolume. LVM growth also needs ec2:CreateVolume, ec2:CreateTags, and ec2:AttachVolume. Parto does not detach or delete EBS volumes.

Local and private-network SSH

For a public test instance, limit inbound SSH to the operator's IP address. When no public IPv4 is assigned, Parto uses the private address instead; the operator must then have VPC routing or use PAS.

Preview means real-cloud validation is still pending

Use a disposable EC2 instance and start with inspect, then grow --dry-run. Whole-disk, partition, and LVM mutation paths must complete golden-path testing before production use.

Parto creates gp3 volumes for LVM in the instance availability zone. Nitro devices are matched by the EBS volume ID exposed as the NVMe serial; Xen-style /dev/xvd* device matching is the fallback. After EBS growth, Parto first checks the kernel-reported size and skips a generic sysfs rescan when Nitro has already exposed the requested capacity. Do not combine --provider aws with the OpenStack-only --cloud or --environment selectors.

04 · MANAGED CONNECTIONS

Import encrypted OpenStack environments

Managed environments let Parto resolve an OpenStack target from PostgreSQL. An environment binds the source cloud profile, immutable project ID, project metadata, region, and encrypted credential payload.

SelectorWhat it selectsCredential source
inspect/grow --provider openstackThe only registered environment, or local discovery when the database is absent or emptyPostgreSQL first, then discovered clouds.yaml or OS_*
inspect/grow --cloud example-cloudA standalone OpenStack profileAutomatically discovered clouds.yaml and optional secure.yaml
environment ... --cloud example-cloudA YAML profile to import or synchronizeExplicit --file and optional --secure-file
inspect/grow --environment production-searchAn imported Parto environment recordEncrypted PostgreSQL payload, decrypted only in memory

--cloud and --environment are both valid for inspect and grow, but they are mutually exclusive.

Safe automatic environment selection

Exactly one registered environment can be used without --environment. If PostgreSQL contains multiple environments, Parto lists their aliases and stops. If it contains none, Parto continues to local OpenStack discovery. Connection, query, master-key, and decryption errors never trigger fallback.

Start PostgreSQL for local development

POSTGRESQL · DOCKER
docker volume create parto-postgres-data

docker run -d \
  --name parto-postgres \
  --restart unless-stopped \
  -e POSTGRES_USER=parto \
  -e POSTGRES_PASSWORD=parto-local-password \
  -e POSTGRES_DB=parto \
  -v parto-postgres-data:/var/lib/postgresql/data \
  -p 127.0.0.1:55432:5432 \
  postgres:17-alpine
CONFIGURE THIS SHELL
export PARTO_DATABASE_URL='postgres://parto:parto-local-password@127.0.0.1:55432/parto?sslmode=disable'
openssl rand -base64 32  # Generate once and save the value.
export PARTO_MASTER_KEY='paste-the-saved-value-here'

Parto creates its environment table on first use. Keep the same master key for every later run; losing or replacing it makes existing credential records unreadable.

Import one project-scoped profile

APPLICATION CREDENTIAL · RECOMMENDED
parto environment import \
  --file ~/.config/openstack/clouds.yaml \
  --cloud example-cloud \
  --name-prefix production-search

parto environment list

Here example-cloud is the profile name in the YAML file, while production-search becomes the Parto environment name. OpenStack application credentials are project-scoped, so import one profile for each project.

Import every project available to a user

MULTI-PROJECT USERNAME/PASSWORD IMPORT
parto environment import \
  --file ~/.config/openstack/clouds.yaml \
  --cloud company \
  --name-prefix production \
  --discover-projects

--discover-projects works with username/password credentials that Keystone can re-scope. Parto imports every enabled accessible project and creates aliases such as production-search. Application credentials are project-scoped, so import those profiles one at a time without --discover-projects.

Find the exact OpenStack project name

In environment list, NAME is the Parto environment alias and may include your prefix. PROJECT is the exact name returned by Keystone, while PROJECT ID is its immutable identifier. You do not need to query PostgreSQL directly to look them up.

FILTER IMPORTED ENVIRONMENTS
parto environment list | grep -i devops

parto environment list --json |
  jq -r '.environments[]
    | select(.project_name | test("devops"; "i"))
    | [.name, .project_name, .project_id, .region]
    | @tsv'

This lists environments already imported into Parto; it does not query Keystone live.

Delete one imported environment explicitly

EXACT-NAME CONFIRMATION
parto environment delete production-search

Parto shows the project metadata and requires the complete environment alias before removing the encrypted PostgreSQL record. Existing operation history is preserved, and environment sync still never deletes missing projects automatically.

Synchronize imported projects safely

PREVIEW, THEN APPLY
parto environment sync \
  --file ~/.config/openstack/clouds.yaml \
  --cloud company \
  --name-prefix production \
  --dry-run

parto environment sync \
  --file ~/.config/openstack/clouds.yaml \
  --cloud company \
  --name-prefix production

Sync matches environments by immutable project ID, adds newly accessible projects, and updates changed metadata or encrypted credentials. Renamed projects keep their Parto aliases. Projects no longer returned by Keystone are marked missing in the result but never deleted automatically.

Synchronization requires username/password credentials that Keystone can re-scope. The required --name-prefix must match the original discovery import and defines the stored environment set being reconciled. Continue importing project-scoped application credentials one at a time.

Credentials stay encrypted

Parto uses AES-256-GCM before writing credential payloads to PostgreSQL, never returns them from environment list, and never writes a temporary managed clouds.yaml. Keep PARTO_MASTER_KEY outside the database.

USE THE IMPORTED ENVIRONMENT
parto inspect \
  --provider openstack \
  --environment production-search \
  --vm-name my-vm \
  --mountpoint /srv/data \
  --ssh-user ubuntu \
  --ssh-key ~/.ssh/id_rsa

If this is the only registered environment, omit the selector:

AUTO-SELECT THE ONLY DATABASE ENVIRONMENT
parto inspect \
  --provider openstack \
  --vm-name my-vm \
  --mountpoint /srv/data \
  --ssh-user ubuntu \
  --ssh-key ~/.ssh/id_rsa

05 · READ ONLY

Inspect before changing anything

inspect discovers eligible data targets and runs preflight checks. It does not accept --size, run mutations, ask for approvals, or write an operation journal.

INSPECT EVERY ELIGIBLE TARGET
parto inspect \
  --provider openstack \
  --environment production-search \
  --vm-name my-vm \
  --ssh-user ubuntu \
  --ssh-key ~/.ssh/id_rsa
INSPECT ONE MOUNTPOINT
parto inspect \
  --provider openstack \
  --environment production-search \
  --vm-name my-vm \
  --mountpoint /srv/data \
  --ssh-user ubuntu \
  --ssh-key ~/.ssh/id_rsa

06 · OBSERVE AND INTEGRATE

Filesystem health and JSON output

Every inspected target includes filesystem capacity and inode pressure. Parto also follows the target to its physical disks and reads SMART health when the VM exposes it.

SignalReported dataBehavior
FilesystemTotal, used, available bytes, and used percentagehealthy below 80%, warning at 80%, critical at 90%
InodesTotal, used, available count, and used percentageUses the same informational thresholds
SMARTPass/fail, model, serial, temperature, and power-on hours when availableunavailable is non-fatal on virtual disks
INSPECT AS JSON
parto inspect \
  --provider openstack \
  --environment production-search \
  --vm-name my-vm \
  --mountpoint /srv/data \
  --ssh-user ubuntu \
  --ssh-key ~/.ssh/id_rsa \
  --json | jq '.targets[].health'
Automation-safe output

--json writes one JSON document to stdout, including the selected provider and automatically detected vm.guest_os. Prompts and operational diagnostics use stderr. Structured errors preserve the normal Parto exit codes.

Readable in a terminal, plain when redirected

Interactive output highlights sections and progress in cyan, successful checks in green, failures in red, and neutral states in yellow. Redirected output stays free of ANSI color codes. Set NO_COLOR=1 or TERM=dumb to disable color explicitly.

SMART is best-effort. Parto uses smartctl when installed, supports older text-only output, and reports each underlying LVM disk separately. Missing or hidden SMART data never makes inspection fail.

07 · PLAN, THEN APPLY

Dry-run and grow

A dry run performs real read-only discovery and prints the planned cloud-provider and guest commands without applying them.

XFS ON A DISK OR PARTITION
parto grow \
  --provider openstack \
  --environment production-search \
  --vm-name my-vm \
  --mountpoint /srv/data \
  --size 110 \
  --ssh-user ubuntu \
  --ssh-key ~/.ssh/id_rsa \
  --dry-run

Here 110 is the new total Cinder volume size in GB.

XFS ON LVM
parto grow \
  --provider openstack \
  --environment production-analytics \
  --vm-name my-vm \
  --mountpoint /data \
  --size 50 \
  --ssh-user ubuntu \
  --ssh-key ~/.ssh/id_rsa \
  --dry-run

Here 50 is the size of a new Cinder volume added to the volume group.

Real run

Remove --dry-run after reviewing the plan. Parto asks for initial approval and confirms every mutating remote command.

08 · PICK THE RIGHT PATH

Filesystem and target selection

XFS is the filesystem. LVM, a partition, or a whole disk is the storage layout underneath it. The recommended selector is always the mounted filesystem: --mountpoint /data.

Internally, cloud operations and filesystem growth now use separate provider and filesystem driver contracts. OpenStack is the established provider and AWS EBS is available as a preview; XFS remains the only filesystem driver.

  • Recommended: --mountpoint /data works for all supported layouts.
  • LVM alternative: --lv-name data-lv accepts the plain LV name.
  • Auto-detect: omit both only when exactly one eligible non-root target exists.

The root filesystem and protected operating-system or runtime trees are never eligible. A target sharing the physical root disk is also rejected.

Protected system paths
//bin/boot/dev/efi/etc/lib/lib64/proc/root/run/sbin/snap/sys/usr/var/lib/snapd/snap/var/lock/var/run

Every descendant of these paths is protected too.

Data locations such as /data, /home, /mnt/storage, /opt/data, /srv, /tmp, and /var remain eligible when every other safety check passes.

09 · IMPORTANT

Understand --size

The meaning changes with the detected storage layout.

XFS placementActionMeaning of --size
On LVMAdd a new Cinder volumeNew volume size in GB
On a partitionGrow volume and partitionNew total volume size in GB
On a whole diskGrow existing volumeNew total volume size in GB
50 GB means two different things

On LVM, --size 50 adds a new 50 GB disk. For an existing 50 GB whole-disk volume that needs 20 GB more, use --size 70.

10 · ACCESS

SSH, sudo, and PAS

Direct SSH with a key

DIRECT SSH
parto inspect \
  --provider openstack \
  --environment production-search \
  --vm-name my-vm \
  --ssh-user ubuntu \
  --ssh-key ~/.ssh/id_rsa

Omit --ssh-key and --ssh-password to prompt for the SSH password. Add --ask-sudo-password when sudo is not passwordless.

PAS gateway

--ssh-gateway is the host you connect to. --ssh-gateway-login is the username template sent to that gateway; it must contain {target}.

PAS DRY RUN
parto grow \
  --provider openstack \
  --environment production-search \
  --vm-name my-vm \
  --mountpoint /srv/data \
  --size 110 \
  --ssh-gateway pas.example.com \
  --ssh-gateway-login 'centrify@{target}@user@example.com' \
  --dry-run

11 · GUARDRAILS

Safety and recovery

  • The root disk and protected OS/runtime mount trees are never touched.
  • Only mounted XFS filesystems are supported; other filesystems stop in preflight.
  • Every mutating command requires operator confirmation.
  • New guest disks are matched to exact Cinder volume IDs by serial.
  • Timeouts and ambiguous outcomes stop the workflow instead of guessing.
  • There is no automatic rollback, detach, delete, or LVM cleanup.

Operation journal

Every grow attempt that selects a target writes a private JSON record with mode 0600. It contains checkpoints and non-secret recovery context.

DEFAULT JOURNAL LOCATION
~/.local/state/parto/runs/
# or ${XDG_STATE_HOME}/parto/runs/

Read operation history from the CLI

LIST, FILTER, AND INSPECT RUNS
parto runs list
parto runs list --status recovery_required --vm-name my-vm --since 24h
parto runs list --json
parto runs show <run-id>
parto runs show <run-id> --json

Run commands are read-only. --since accepts a duration such as 24h or an RFC3339 timestamp. Journal schema versioning keeps records created before v0.2.0 readable.

Ctrl-C before mutation is a normal cancellation. After mutation begins, the result may be unknown; Parto stops and prints read-only inspection commands and manual recovery guidance. With --json, the same recovery context, failed step, inspection commands, and guidance are returned as structured fields.

12 · REFERENCE

CLI reference

OptionPurpose
--providerRequired for inspect/grow: explicitly select openstack or preview aws. There is no default.
--vm-nameInstance name on the selected provider. AWS uses an exact EC2 Name tag. Required for every command.
--sizeGrow only. Positive integer in GB; meaning depends on layout.
--mountpointRecommended target selector, such as /data.
--lv-nameAlternative LVM selector using a plain LV name.
--cloudOpenStack only: explicitly bypass database lookup and use a profile from an automatically discovered clouds.yaml.
--environmentOpenStack only: explicitly use an imported environment stored in PostgreSQL. Multiple registered environments require this selector.
environment ... --cloudSelect a profile from an explicit YAML file during import or sync.
environment import --discover-projectsImport every enabled project accessible to re-scopeable username/password credentials.
--ssh-userRequired for direct SSH. Use the image account, commonly ubuntu or ec2-user; Parto detects the guest OS after login.
--ssh-keySSH private-key path.
--ssh-passwordNon-interactive SSH password; interactive prompt is safer.
--ask-sudo-passwordPrompt interactively for the remote sudo password.
--ssh-gatewayPAS gateway hostname.
--ssh-gateway-loginPAS username template containing {target}.
--dry-runRead the real state and print mutations without applying them.
--jsonWrite one machine-readable result document to stdout.
COMMAND HELP
parto inspect --help
parto grow --help
parto environment --help
parto environment import --help
parto environment list --help
parto environment delete --help
parto environment sync --help
parto runs --help

NEED A HAND?

Talk storage with us.

Talk to an engineer