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.
chmod +x parto_linux_amd64
sudo mv parto_linux_amd64 /usr/local/bin/parto
parto --versionchmod +x parto_darwin_arm64
sudo mv parto_darwin_arm64 /usr/local/bin/parto
parto --versionNew-Item -ItemType Directory -Force "$HOME\bin"
Move-Item .\parto_windows_amd64.exe "$HOME\bin\parto.exe"
& "$HOME\bin\parto.exe" --versionUse 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.
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.
make verify
make build
./dist/parto --versionStart with read-only inspection. Then run a size-specific dry run. Remove --dry-run only after both outputs look correct.
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.
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.
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.
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.
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.
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.
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_*.
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.
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.
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: trueclouds:
example-cloud:
auth:
password: replace-with-your-openstack-passwordchmod 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-vmFor 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.
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_rsaparto inspect \
--provider aws \
--vm-name search-amzn-01 \
--mountpoint /data \
--ssh-user ec2-user \
--ssh-key ~/.ssh/parto-amazon-linux.pemParto 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.
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.
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.
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.
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.
| Selector | What it selects | Credential source |
|---|---|---|
inspect/grow --provider openstack | The only registered environment, or local discovery when the database is absent or empty | PostgreSQL first, then discovered clouds.yaml or OS_* |
inspect/grow --cloud example-cloud | A standalone OpenStack profile | Automatically discovered clouds.yaml and optional secure.yaml |
environment ... --cloud example-cloud | A YAML profile to import or synchronize | Explicit --file and optional --secure-file |
inspect/grow --environment production-search | An imported Parto environment record | Encrypted PostgreSQL payload, decrypted only in memory |
--cloud and --environment are both valid for inspect and grow, but they are mutually exclusive.
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
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-alpineexport 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
parto environment import \
--file ~/.config/openstack/clouds.yaml \
--cloud example-cloud \
--name-prefix production-search
parto environment listHere 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
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.
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
parto environment delete production-searchParto 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
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 productionSync 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.
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.
parto inspect \
--provider openstack \
--environment production-search \
--vm-name my-vm \
--mountpoint /srv/data \
--ssh-user ubuntu \
--ssh-key ~/.ssh/id_rsaIf this is the only registered environment, omit the selector:
parto inspect \
--provider openstack \
--vm-name my-vm \
--mountpoint /srv/data \
--ssh-user ubuntu \
--ssh-key ~/.ssh/id_rsa05 · 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.
parto inspect \
--provider openstack \
--environment production-search \
--vm-name my-vm \
--ssh-user ubuntu \
--ssh-key ~/.ssh/id_rsaparto inspect \
--provider openstack \
--environment production-search \
--vm-name my-vm \
--mountpoint /srv/data \
--ssh-user ubuntu \
--ssh-key ~/.ssh/id_rsa06 · 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.
| Signal | Reported data | Behavior |
|---|---|---|
| Filesystem | Total, used, available bytes, and used percentage | healthy below 80%, warning at 80%, critical at 90% |
| Inodes | Total, used, available count, and used percentage | Uses the same informational thresholds |
| SMART | Pass/fail, model, serial, temperature, and power-on hours when available | unavailable is non-fatal on virtual disks |
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'--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.
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.
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-runHere 110 is the new total Cinder volume size in GB.
parto grow \
--provider openstack \
--environment production-analytics \
--vm-name my-vm \
--mountpoint /data \
--size 50 \
--ssh-user ubuntu \
--ssh-key ~/.ssh/id_rsa \
--dry-runHere 50 is the size of a new Cinder volume added to the volume group.
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 /dataworks for all supported layouts. - LVM alternative:
--lv-name data-lvaccepts 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.
//bin/boot/dev/efi/etc/lib/lib64/proc/root/run/sbin/snap/sys/usr/var/lib/snapd/snap/var/lock/var/runEvery 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 placement | Action | Meaning of --size |
|---|---|---|
| On LVM | Add a new Cinder volume | New volume size in GB |
| On a partition | Grow volume and partition | New total volume size in GB |
| On a whole disk | Grow existing volume | New total volume size in GB |
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
parto inspect \
--provider openstack \
--environment production-search \
--vm-name my-vm \
--ssh-user ubuntu \
--ssh-key ~/.ssh/id_rsaOmit --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}.
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-run11 · 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.
~/.local/state/parto/runs/
# or ${XDG_STATE_HOME}/parto/runs/Read operation history from the CLI
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> --jsonRun 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
| Option | Purpose |
|---|---|
--provider | Required for inspect/grow: explicitly select openstack or preview aws. There is no default. |
--vm-name | Instance name on the selected provider. AWS uses an exact EC2 Name tag. Required for every command. |
--size | Grow only. Positive integer in GB; meaning depends on layout. |
--mountpoint | Recommended target selector, such as /data. |
--lv-name | Alternative LVM selector using a plain LV name. |
--cloud | OpenStack only: explicitly bypass database lookup and use a profile from an automatically discovered clouds.yaml. |
--environment | OpenStack only: explicitly use an imported environment stored in PostgreSQL. Multiple registered environments require this selector. |
environment ... --cloud | Select a profile from an explicit YAML file during import or sync. |
environment import --discover-projects | Import every enabled project accessible to re-scopeable username/password credentials. |
--ssh-user | Required for direct SSH. Use the image account, commonly ubuntu or ec2-user; Parto detects the guest OS after login. |
--ssh-key | SSH private-key path. |
--ssh-password | Non-interactive SSH password; interactive prompt is safer. |
--ask-sudo-password | Prompt interactively for the remote sudo password. |
--ssh-gateway | PAS gateway hostname. |
--ssh-gateway-login | PAS username template containing {target}. |
--dry-run | Read the real state and print mutations without applying them. |
--json | Write one machine-readable result document to stdout. |
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 --helpNEED A HAND?