CLI tool for deploying software to remote and local servers via SSH/SFTP.
Project description
ikctl — Install Kit Control
CLI tool to deploy and execute bash scripts ("kits") on remote servers via SSH/SFTP or on the local machine. Supports parallel execution, DAG-based pipeline orchestration, and git-based kit repositories.
How it works
A kit is a folder containing bash scripts and an ikctl.yaml manifest. ikctl uploads the scripts to the remote server via SFTP and executes them over SSH.
A pipeline is a YAML file that chains multiple kits across different server groups, with dependencies, parallel execution and output passing between steps.
Everything is driven by ~/.ikctl/config, which defines one or more contexts (server groups, kits source, secrets).
~/.ikctl/config ← main config: contexts, active context
~/kits/
config.yaml ← server groups
show-date/
ikctl.yaml ← kit manifest (uploads + pipeline + outputs)
date.sh ← the actual script
~/kits/pipelines/
deploy.yaml ← pipeline YAML
Kits are auto-discovered — no index file needed. Any folder under path_kits that contains an ikctl.yaml is a kit.
Requirements
- Python 3.13+
- git (for git-based kit repositories)
Installation
Recommended — pipx (isolated, no system conflicts):
pipx install ikctl
With uv:
uv tool install ikctl
With pip (on Debian/Ubuntu you may need --break-system-packages):
pip install ikctl --break-system-packages
From source:
git clone <repo>
cd ikctl
uv sync
uv run ikctl --version
Initial setup
On first run, ikctl creates ~/.ikctl/ and ~/kits/ automatically.
Minimal ~/.ikctl/config:
context: production
contexts:
production:
path_kits: ~/kits # where your kits live (auto-discovered)
path_servers: ~/kits # where config.yaml lives
path_secrets: ~/.passwords/pw # file containing the sudo/ssh password
path_pipelines: ~/kits/pipelines # optional: pipelines directory
mode: remote
timeout_connect: 30.0 # SSH connect timeout (seconds)
timeout_exec: 120.0 # local command timeout (seconds)
exclude: # optional: kits to hide from discovery
- internal/debug-kit
Git-based kits (optional)
Pull kits from a git repository instead of a local directory:
contexts:
production:
kits_repo: git@gitlab.com:company/kits.git # SSH repo
kits_ref: main # branch, tag or commit
# kits_token: $GITLAB_TOKEN # for private HTTPS repos
path_servers: ~/kits/production
path_secrets: ~/.passwords/password
mode: remote
For private HTTPS repos:
kits_repo: https://gitlab.com/company/kits.git
kits_token: $GITLAB_TOKEN # references an env variable
export GITLAB_TOKEN=glpat-xxxxxxxxxxxx
ikctl --list kits # clones on first use, pulls on subsequent runs
Server config
# ~/kits/config.yaml
servers:
- name: workers
user: ubuntu
hosts: [10.0.0.10, 10.0.0.11, 10.0.0.12]
port: 22
pkey: ~/.ssh/id_ed25519 # key-based auth
- name: master
user: ubuntu
hosts: [10.0.0.5]
port: 22
pkey: ~/.ssh/id_ed25519
- name: db
user: root
hosts: [10.0.0.20]
port: 22
password: $PASSWORD # password auth (env var)
SSH authentication
| Config key | Auth method | Notes |
|---|---|---|
pkey: ~/.ssh/id_ed25519 |
Public key only | Ed25519, ECDSA or RSA |
password: $PASSWORD |
Password only | |
| Neither | SSH agent + ~/.ssh/ discovery |
pkey and password are mutually exclusive — only one is attempted.
Kit structure
# ~/kits/kubeadm-init/ikctl.yaml
kits:
uploads:
- kubeadm-init.sh # files to upload to the remote server
pipeline:
- kubeadm-init.sh # scripts to execute in order
outputs: # optional: documents KEY=VALUE the script emits
JOIN_TOKEN: "kubeadm join token for worker nodes"
JOIN_ENDPOINT: "API server endpoint (IP:6443)"
The outputs: field documents what KEY=VALUE lines the script writes to stdout. These values are available to subsequent pipeline steps via {{ steps.<id>.KEY }}.
Usage
ikctl [-l {kits,servers,context,mode,pipelines}] [-i INSTALL] [-n NAME]
[-p [PARAM ...]] [-s {sudo}] [-c CONTEXT] [-m {local,remote}]
[-v] [--dry-run] [--debug] [--describe KIT]
[--pipeline FILE] [--timeout-connect F] [--timeout-exec F]
[--parallel-workers N]
List
ikctl --list kits # all discovered kits (shows declared outputs)
ikctl --list servers # configured server groups
ikctl --list context # active context and all paths
ikctl --list pipelines # pipeline YAML files in path_pipelines
Inspect a kit
ikctl --describe kubeadm-init
Shows the kit's scripts and declared outputs:
╭─ Kit ───────────────╮
│ kubeadm-init │
╰─────────────────────╯
Uploads: kubeadm-init.sh
Pipeline: kubeadm-init.sh
Outputs:
JOIN_TOKEN → kubeadm join token for worker nodes
JOIN_ENDPOINT → API server endpoint (IP:6443)
Switch context
ikctl --context staging
Run a kit
# Remote (default)
ikctl --install show-date --name workers
# With parameters passed to the script
ikctl --install deploy --name workers -p v2.1.0
# With sudo
ikctl --install install-docker --name workers --sudo sudo
# Local machine (no SSH)
ikctl --install show-date --mode local
# Preview without executing
ikctl --install deploy --name workers -p v2.1.0 --dry-run
Parallel execution
When a server group has multiple hosts, ikctl runs them concurrently:
ikctl --install show-date --name workers --parallel-workers 4
A spinner appears while connecting to each host and a progress bar during uploads. Output is prefixed with the host IP.
Pipeline orchestration
A pipeline chains kits across server groups with DAG dependencies and output passing:
# ~/kits/pipelines/install-kubernetes.yaml
name: install-kubernetes
steps:
- id: prepare
kit: packages
servers: all-nodes
sudo: true
- id: init-master
kit: kubeadm-init
servers: master
sudo: true
needs: [prepare]
params:
- "{{ params.POD_CIDR }}" # from CLI: -p POD_CIDR=192.168.0.0/16
- id: join-workers
kit: join-in-kubes
servers: workers
sudo: true
needs: [init-master]
params:
- "{{ steps.init-master.JOIN_TOKEN }}" # output from previous step
- "{{ steps.init-master.JOIN_ENDPOINT }}" # output from previous step
# Run pipeline by name (requires path_pipelines in config)
ikctl --pipeline install-kubernetes -p POD_CIDR=192.168.0.0/16
# Or by full path
ikctl --pipeline ~/kits/pipelines/install-kubernetes.yaml -p POD_CIDR=192.168.0.0/16
# Preview
ikctl --pipeline install-kubernetes -p POD_CIDR=192.168.0.0/16 --dry-run
Steps without needs run in parallel. Steps fail gracefully — dependents are marked as skipped.
Debug mode
# Show all internal logs (paramiko included)
ikctl --install show-date --name workers --debug
Without --debug, only useful output is shown (Rich formatted).
Timeouts
ikctl --install show-date --name workers --timeout-connect 60
ikctl --install build --mode local --timeout-exec 300
Or set per context in ~/.ikctl/config with timeout_connect and timeout_exec.
License
Apache License 2.0 — see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ikctl-1.4.2.tar.gz.
File metadata
- Download URL: ikctl-1.4.2.tar.gz
- Upload date:
- Size: 156.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
845e27f692992c20042835bd9ed6d952dcde0f557d53793a421861b5304f80bc
|
|
| MD5 |
e982f1e6d96657300d10bd862b7416c6
|
|
| BLAKE2b-256 |
222b9defa0148ad97c27fc4aea3efbdffdeaff2684cef457c5a0a7daf3efd6fb
|
File details
Details for the file ikctl-1.4.2-py3-none-any.whl.
File metadata
- Download URL: ikctl-1.4.2-py3-none-any.whl
- Upload date:
- Size: 47.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
002e21c402bbf54fe7e2de011f8b2411e5ba6711b33173ccb3cea597b90043a1
|
|
| MD5 |
ae287e263a09535dee5b3d9f4a6d625b
|
|
| BLAKE2b-256 |
ee956f22b6d9f64b2db87e325610a6e348869766bf4d9a4fcedd5af42e840764
|