Skip to content

casudo/KiwiSSH

Logo

KiwiSSH

Backup your network device configurations with ease and keep track of changes.

GitHub License GitHub release (with filter) GitHub action checks GitHub issues GitHub last commit

About KiwiSSH

KiwiSSH is a network device configuration backup tool that connects to your devices via SSH/Telnet, fetches their configurations, and stores them in git repositories for easy version control and change tracking. It also provides a user-friendly web interface to manage your devices, view backup logs, and see configuration changes over time.

It was created as better alternative to RANCID and Oxidized, with a focus on simplicity, ease of use, and modern technologies.

Warning

KiwiSSH is still in development and may contain bugs and support for only a limited number of vendors. If you want to collaborate, contribute or just have questions, please open an issue or PR.

Table of Contents

Features

The frontend offers:

  • Dashboard (statistics, favorite devices, overview of KiwiSSH)
  • Information-rich device list with filtering, search and favorite option, backup contribution calender, group and vendor belonging, status badge, config diff view and more
  • Overview of configured SSH profiles, vendors and groups for easy management and device tracking
  • Detailed backup job logs with timestamps, status and error messages for each backup attempt
  • Swagger API documentation

The backend provides:

  • SSH/Telnet connectivity to any kind devices for configuration backup
  • Configurable backup schedules with cron expressions on global, group and device level
  • Local git repositories for each device group to store configuration history and provide diff view
  • Optional remote git repository support to push configuration changes to a central Git server (e.g. GitHub, GitLab, Gitea, etc.)
  • Support for loading device lists from CSV or Ansible inventory files, PostgreSQL databases or HTTP APIs
  • Configurable SSH/Telnet options and backup commands per vendor, group and device, making it flexible to support a wide range of devices
  • Configurable processing rules for captured configurations, including line stripping, cropping and regex-based redaction of sensitive information
  • The ability to easily add support for new vendors by creating YAML configuration files that define how to interact with the device CLI and process the output
  • RESTful API endpoints for device management, backup job logs and configuration retrieval to enable integration with other tools and automation

Supported OS/Device Types

For a detailed overview of supported OS/Device types, please refer to the OS_TYPES.md file.

Screenshots

Note

Screenshots as of version v1.0.0

Dashboard

Device List

Vendors

Groups

SSH Profiles

Backup Job Logs


Installation

Bare Metal

To run KiwiSSH on your local machine without Docker, follow these steps:

  1. Clone the repository
  2. Navigate to the backend directory and install the required Python dependencies from requirements.txt
  3. Copy kiwissh.yaml.example to config/kiwissh.yaml and configure it
  4. Run the backend using python entrypoint.py
  5. Navigate to the frontend directory and install the dependencies with npm install
  6. Start the frontend with npm run dev

Docker

KiwiSSH uses separate Docker images for backend and frontend. The example compose stack also includes PostgreSQL for the application database. If you already have a PostgreSQL database up and running, you may remove the service from the compose file.

  • Backend: FastAPI API service
  • Frontend: Nginx serving the built Vue app and proxying /api/* to backend
  • PostgreSQL: Application data storage
  1. Update the docker-compose.yaml.example with the correct host paths, network, database credentials, and any desired environment variables or volume mounts. Use the same PostgreSQL connection values in your kiwissh.yaml.
  2. Run the docker-compose.yaml.example file.
  3. Open the UI at http://<IP>:8123.

Important

Persist the PostgreSQL data and KiwiSSH backup directories on the host. Data stored only inside a container is lost when that container is recreated.

Important

If you're using SSH key authentication (remote git push, device backup auth, or jumphost auth), mount your SSH material into /home/kiwissh/.ssh and ensure permissions are correct (typically 600 for private keys/config/known_hosts, owned by kiwissh uid:gid 1000:1000).

Note

  • API calls are available through the frontend proxy: http://<IP>:8123/api/v1/...
  • The backend image includes default ssh_profiles.yaml and vendors/*.yaml directly in /config
  • The backend always reads configuration from /config
  • You still need a valid kiwissh.yaml in /config and should persist backups/ on a host volume

Configuration

KiwiSSH can be configured using a combination of environment variables and a YAML configuration file. The YAML file contains the main configuration settings, while environment variables are used to define global, application-unspecific values for different deployments.

Environment Variables

See the example backend/.env.example file. Either rename it to .env and fill in the values or set the environment variables directly in your deployment environment (e.g. Docker, systemd, etc.). KiwiSSH will automatically load these environment variables on startup.

Tip

Environment variables always take precedence over the defaults defined in config.py.

Warning

Only the operational top-level settings in the config.py file can be overridden by environment variables. Nested and re-computed fields like database_url cannot be overridden!

Variable Name Description Required Default Value
KIWISSH_CONFIG_DIR Directory KiwiSSH reads its configuration from (kiwissh.yaml, ssh_profiles.yaml, vendors/). Defaults to /config in Docker, or the backend's bundled config/ directory on bare-metal installs. No /config (Docker) / ../backend/config (bare-metal)
KIWISSH_LOCAL_TEST_MODE If set to true, the application will run in local test mode, which enforces certain config values for easier local testing and development. No false
TZ Timezone for the application. This is used for timestamps in backup job logs and Git commit messages. No UTC

kiwissh.yaml

Important

Make sure to rename the example file kiwissh.yaml.example to kiwissh.yaml. Your kiwissh.yaml is git-ignored so it won't conflict with future git pulls.

Below you'll find a detailed overview of all available configuration options in the kiwissh.yaml file. This file is the main configuration file for KiwiSSH and contains settings for the application, database connection, device sources, git integration, groups and nodes.

Important

Changes to the kiwissh.yaml file will require a restart of the backend and frontend applications to take effect.

app

Full available options:

Key Description Required Default Value
app.debug If set to true, the application will run in debug mode, which enables more verbose logging No false
app.threads The maximum number of concurrent SSH sessions for backups. No 20
app.timeout The global connection timeout in seconds. This can be overridden for specific groups or nodes. No 30
app.retry The global retry count, which defines how many additional attempts should be made after the first failed attempt. This can also be overridden for specific groups or nodes. No 3
app.protocol Default protocol for device connections (ssh or telnet). No ssh
app.api.host The host on which the API server will run. Should be set to 0.0.0.0 when running in Docker. No 127.0.0.1
app.api.port The port on which the API server will run. No 8000
app.api.cors_origins A list of allowed CORS origins for the API server. No ["http://localhost:5173", "http://127.0.0.1:5173"]
app.schedule.cron The cron expression for the global backup schedule. No 0 2 * * *
app.schedule.TZ The timezone for the backup schedule. No TZ environment variable or UTC
app.retention.enabled If set to true, log retention policies will be enforced. Will run every day at 03:00 UTC. No false
app.retention.max_age_days Maximum age of backup job logs in days. Logs older than this will be deleted. No 90
app.retention.max_rows Maximum number of backup job logs to keep. Oldest logs will be deleted when this limit is exceeded. No 100000

application_database

The application_database segment is used to configure the connection to the PostgreSQL database where KiwiSSH will store its application data (e.g. backup job logs, favorite devices, etc.). This database is required for KiwiSSH to function properly.

Important

Make sure that the database exists and that the provided user has the necessary permissions to create tables and perform operations on the database. KiwiSSH will automatically create the required tables on startup.

Key Description Required Default Value
application_database.host The host of the PostgreSQL database. Yes -
application_database.port The port of the PostgreSQL database. No 5432
application_database.database The name of the PostgreSQL database. Yes -
application_database.username The username for the PostgreSQL database. Yes -
application_database.password The password for the PostgreSQL database. Yes -

notifications

You can get notified if the backup of a device fails or succeeds.

When to notify (trigger)?

  • always: Notify on every backup attempt regardless of the outcome (success or failure).
  • failure: Notify on every failed backup attempt, regardless of previous history.
  • failure_new: Notify on a failed backup attempt only if the previous backup was "Successful", "No Changes", or if there is no previous history (first ever backup failure). This avoids notification fatigue by only alerting on new failures.
Key Description Required Default Value
notifications.enabled Whether notifications are enabled. No False
notifications.trigger The notification trigger. No failure
notifications.type The type of notification. Yes -
notifications.smtp.host The SMTP server host. Yes -
notifications.large_diff_threshold The diff line count threshold for considering a backup change as "large". This can be used to trigger notifications only for significant changes. No 650
notifications.smtp.port The SMTP server port. No 25
notifications.smtp.sender The sender email address. Yes -
notifications.smtp.recipients The recipient email addresses (list). Yes -
notifications.smtp.username The username for the SMTP server. No -
notifications.smtp.password The password for the SMTP server. No -
notifications.smtp.use_tls Whether to use STARTTLS. Change the port accordingly. No False
notifications.smtp.use_ssl Whether to use SSL/TLS. Change the port accordingly. No False

sources

Configure where KiwiSSH load the devices to backup from. You can choose between loading the devices from a CSV file, an Ansible inventory file, a PostgreSQL database, or a generic HTTP endpoint.

Important

At least one source must be configured (and only one). The following fields are required for every device in the source: group, device_name, ip_address, enabled.

CSV File

Key Description Required Default Value
sources.file Path to the CSV file containing the device entries. Absolute, or relative to the config directory. Yes -

The devices will be loaded from the specified CSV file. The headers must be separated by commas like this:

See an example CSV file here.

PostgreSQL

Important

Make sure that the database exists and that the provided user has the necessary permissions to read from the database.

Key Description Required Default Value
sources.postgres.host The host of the PostgreSQL database containing the device entries. Yes -
sources.postgres.port The port of the PostgreSQL database containing the device entries. No 5432
sources.postgres.database The name of the PostgreSQL database containing the device entries. Yes -
sources.postgres.table The name of the PostgreSQL table containing the device entries. Yes -
sources.postgres.username The username for the PostgreSQL database containing the device entries. Yes -
sources.postgres.password The password for the PostgreSQL database containing the device entries. Yes -

The backup_jobs table can grow quite large over time depending on the number of devices and backup frequency. To prevent the database from growing indefinitely, it's recommended to set up a regular maintenance job to clean up old backup job logs that are no longer needed. This can be done using a simple SQL query to delete old records based on a retention policy (e.g., delete logs older than 90 days). We might implement this as a built-in feature in the future, but for now it's up to the user to set this up.

Tip

Consider backing up your PostgreSQL database(s) regularly, independently of KiwiSSH. We recommend Databasus for that.

HTTP

Loads devices from an HTTP(S) endpoint that returns a JSON array of device objects.

Key Description Required Default Value
sources.http.url The HTTP(S) URL to fetch the device list from. Must start with http:// or https://. Yes -
sources.http.headers Map of HTTP request headers, e.g. an authentication token. No {}
sources.http.map Maps KiwiSSH's canonical fields (device_name, ip_address, group, enabled) to the field names in the JSON response. Yes -
sources.http.default_group Fallback group used when a response item has no (mapped) group value. No -
sources.http.items_key Key to locate the device list when the response is an object instead of a top-level array. No -
sources.http.verify_tls Whether to verify the server's TLS certificate. No true
sources.http.timeout Request timeout in seconds. No 10

As with the file and PostgreSQL sources, Vendors, credentials and SSH profile are not taken from the HTTP response — they are resolved from the matching group (and optional per-device node) configuration in kiwissh.yaml. The HTTP source only provides group, device_name, ip_address and enabled.

Whatever the outer shape, the device list must ultimately be a JSON array of flat objects, where each object holds the fields referenced by sources.http.map. KiwiSSH reads the mapped field names as direct keys on each item — it does not descend into nested objects or use object keys as device names.

The examples below all assume this map:

map:
  device_name: hostname
  ip_address: ip
  group: group

1. Top-level JSON array (no items_key needed)

[
  { "hostname": "device-01",  "ip": "192.168.1.2", "group": "fortigate-fws" },
  { "hostname": "device-02", "ip": "172.16.1.3", "group": "customer-abc-cisco-switch" }
]

2. Array nested under a key (set items_key to that key)

{
  "devices": [
    { "hostname": "device-01",  "ip": "192.168.1.2", "group": "fortigate-fws" },
    { "hostname": "device-02", "ip": "172.16.1.3", "group": "customer-abc-cisco-switch" }
  ]
}
sources:
  http:
    url: "https://example.com/api/devices"
    items_key: devices
    map:
      device_name: hostname
      ip_address: ip
      group: group

Warning

When an item has no mapped group value (missing or blank), the configured sources.http.default_group is applied. Similarly, when no enabled field is mapped or present, the device defaults to enabled.

Caution

Not supported: an array whose items are keyed by device name (e.g. [{ "device-01": { ... }, "device-02": { ... } }]) or any structure where the mapped fields are not direct keys of each list item. The object keys carry no meaning to KiwiSSH — the device name always comes from the mapped device_name field.

Tip

Since the HTTP source is generic, you can use it to load devices from any system that can provide a JSON array of devices. For example, you could use LibreNMS, NetBox, or any other inventory system that has an API endpoint returning device information in JSON format.

See how to implement LibreNMS support here.

Ansible Inventory

Key Description Required Default Value
sources.ansible Path to the Ansible inventory YAML file containing the device entries. Absolute, or relative to the config directory. Yes -

The devices will be loaded from the specified Ansible inventory YAML file. The hosts must be defined under the appropriate groups, and the ansible_host variable must be present (will be used as the IP address).

See an example Ansible inventory YAML file here.

git

KiwiSSH will always store the device configurations in local git repositories to provide diff view, history and download options. You can optionally set a remote git location to push the commits (the device config) to.

Caution

DO NOT delete the "backups" folder ever unless you want to lose all your local git repositories and their history.

Key Description Required Default Value
git.local_path The local path where the git repositories for the device groups will be stored. Absolute, or relative to the config directory. No /config/backups
git.commit_message_template The global template for the git commit messages. Available placeholders: {group}, {device_name}, {timestamp}. No "Backup: {group}/{device_name} at {timestamp}"
git.remote.url The global remote git repository URL. Available placeholders: {group}. This can be overridden for specific groups. No -
git.remote.branch The global remote git branch to push to. This can be overridden for specific groups. No main

groups

Key Description Required Default Value
groups.<group>.username The username for SSH authentication for devices in this group. Yes -
groups.<group>.password The password for SSH authentication for devices in this group (optional when ssh_key_file is used). No -
groups.<group>.enable_password Optional enable password for devices in this group. Vendor YAML then values can reference it with {{ enable_password }}. No -
groups.<group>.ssh_key_file The private key file path for SSH authentication for devices in this group (alternative to password). No -
groups.<group>.ssh_profile The SSH profile to use for devices in this group. This is used to determine the SSH options to use when connecting to the devices. Yes -
groups.<group>.protocol Protocol to use for devices in this group (ssh or telnet). No Global app.protocol
groups.<group>.port Protocol port for devices in this group. No 22 (or 23 when protocol: telnet)
groups.<group>.vendor The vendor of the devices in this group. This is used to determine the CLI commands to run for fetching the configuration. Yes -
groups.<group>.jumphost.hostname Jumphost hostname or IP for this group. If set, devices in this group are reached through this jumphost. Yes, if jumphost -
groups.<group>.jumphost.port Jumphost SSH port. No 22
groups.<group>.jumphost.username Jumphost SSH username. Yes, if jumphost -
groups.<group>.jumphost.password Jumphost SSH password (optional when groups.<group>.jumphost.ssh_key_file is used). No -
groups.<group>.jumphost.ssh_key_file Jumphost private key file path (alternative to jumphost password). No -
groups.<group>.jumphost.ssh_profile SSH profile for the jumphost connection. This is separate from groups.<group>.ssh_profile (device connection profile) and is required when a jumphost is configured. Yes, if jumphost -
groups.<group>.timeout The SSH timeout in seconds for devices in this group. This overrides the global SSH timeout. No Global app.timeout
groups.<group>.retry The SSH retry count for devices in this group. This overrides the global SSH retry count. No Global app.retry
groups.<group>.schedule.cron The cron expression for the backup schedule for devices in this group. This overrides the global backup schedule. No Global app.schedule.cron
groups.<group>.git.commit_message_template The git commit message template for this group. This overrides global git.commit_message_template. No Global git.commit_message_template
groups.<group>.git.remote.url The remote git repository URL for this group. This overrides the global git.remote.url. No Global git.remote.url or if set globally
groups.<group>.git.remote.branch The remote git branch to push to for this group. This overrides the global git.remote.branch. No Global git.remote.branch if set globally

nodes

Important

Group cannot be overridden on node level.

Key Description Required Default Value
nodes.<device_name>.username The username for SSH authentication for this device. No groups.<group>.username
nodes.<device_name>.password The password for SSH authentication for this device. No groups.<group>.password
nodes.<device_name>.enable_password Optional enable password for this device. This overrides groups.<group>.enable_password and can be referenced from vendor YAML then values with {{ enable_password }}. No groups.<group>.enable_password
nodes.<device_name>.ssh_key_file The private key file path for SSH authentication for this device. No groups.<group>.ssh_key_file
nodes.<device_name>.ssh_profile The SSH profile to use for this device. This is used to determine the SSH options to use when connecting to the device. No groups.<group>.ssh_profile
nodes.<device_name>.protocol Protocol override for this device (ssh or telnet). No groups.<group>.protocol or Global app.protocol
nodes.<device_name>.port Port override for this device. No groups.<group>.port or 22 (or 23 when protocol: telnet)
nodes.<device_name>.vendor The vendor of this device. This is used to determine the CLI commands to run for fetching the configuration. No groups.<group>.vendor
nodes.<device_name>.jumphost.hostname Node-level jumphost hostname/IP override. Yes, if jumphost groups.<group>.jumphost.hostname
nodes.<device_name>.jumphost.port Node-level jumphost SSH port override. No groups.<group>.jumphost.port or 22
nodes.<device_name>.jumphost.username Node-level jumphost username override. Yes, if jumphost groups.<group>.jumphost.username
nodes.<device_name>.jumphost.password Node-level jumphost password override. No groups.<group>.jumphost.password
nodes.<device_name>.jumphost.ssh_key_file Node-level jumphost key file override. No groups.<group>.jumphost.ssh_key_file
nodes.<device_name>.jumphost.ssh_profile Node-level jumphost SSH profile override. Yes, if jumphost groups.<group>.jumphost.ssh_profile
nodes.<device_name>.timeout The SSH timeout in seconds for this device. This overrides the group and global SSH timeout. No groups.<group>.timeout or Global app.timeout
nodes.<device_name>.retry The SSH retry count for this device. This overrides the group and global SSH retry count. No groups.<group>.retry or Global app.retry
nodes.<device_name>.schedule.cron The cron expression for the backup schedule for this device. This overrides the group and global backup schedule. No groups.<group>.schedule.cron or Global app.schedule.cron
nodes.<device_name>.git.commit_message_template The git commit message template for this device. This overrides group and global git commit templates. No groups.<group>.git.commit_message_template or global git.commit_message_template

Vendor folder

Found at /config/vendors

Vendor YAML files define how KiwiSSH interacts with each device CLI and how captured output is processed before it is saved.

Important

Only the {{ enable_password }} placeholder is supported, and only in then values. If it resolves to an empty string (""), KiwiSSH sends Enter.

Tip

You can create your own vendor YAML file by copying one of the existing ones and modifying it according to the CLI output of your devices. If you want to contribute your vendor file to the project, please create a Pull Request with the new vendor YAML file in the config/vendors folder.

Important

You should read the contents of the vendor YAML files for the device types you want to backup to understand which CLI commands are used to fetch the configuration and how the output is processed. You can always add your own processing.strip_patterns or redaction.patterns to further customize the output processing for your specific devices if needed.

Vendor YAML file

Each vendor file contains these top-level sections:

  • vendor: metadata (id, name, description)
  • session: session-level output settings (comment_prefix, prompt, pagination, login, include_metadata_in_config)
  • commands: command phases (pre_backup, backup, post_backup); you can nest protocol-specific overrides under commands.ssh or commands.telnet
  • processing: optional output cleanup/redaction rules

Each segment is explained in detail below.

[TIP] You can override the vendor for a specific device in kiwissh.yaml under nodes.<device_name>.vendor.

vendor

Key Description Required Default Value
vendor.id A unique identifier for the vendor. Yes -
vendor.name The name of the vendor. Yes -
vendor.description A brief description of the vendor. No -

session

Key Description Required Default Value
session.comment_prefix If set, command outputs will be prefixed with this string and rendered as comments in the saved config file. This is useful for adding metadata like command descriptions or timestamps directly in the config file. No !
session.prompt Optional prompt regex (or list of regexes) used to detect command completion in interactive shells. Match should cover the full prompt line. If omitted or invalid, KiwiSSH falls back to the built-in generic prompt pattern. No [^\r\n=]*[A-Za-z0-9][^\r\n=]*[>#]\s*$
session.login Optional list of interactive login steps executed inside the SSH shell before backup commands run. Used for devices that require a second CLI login (e.g. a User Name: / Password: prompt) after the SSH connection is established. Each step is { expect: "<regex>", send: "<value>" }; steps run in order. The placeholders {{ username }} and {{ password }} are replaced with the device's credentials configured on the group/node level. Omit entirely for devices that present a prompt immediately. No -
session.pagination.enabled Enables pagination prompt handling for this vendor. No false
session.pagination.patterns Pagination pattern config. Supports a string, a list of regex strings, or a list of objects like { pattern: "...", response: "..." }. Matching uses contains-style regex search against the normalized output line. When using object entries, response is configured per pattern and should be preferred. If session.pagination is omitted entirely, KiwiSSH falls back to built-in standard pagination detection patterns. No Built-in standard pagination pattern list
session.include_metadata_in_config Controls whether outputs from metadata: true commands are prepended as a block in the saved config. Metadata is always present in the backup job log. No false

commands

Key Description Required Default Value
commands.<phase> A list of commands to run in the specified phase. Supported phases are pre_backup, backup, and post_backup. Yes -

Steps run in a single interactive shell session in this order: pre_backup -> backup -> post_backup

Each item in the commands.<phase> list follows this structure:

commands:
  backups:
    - command: "show running-config" # simple command
      description: "Get running configuration"
    - command: "another command here" # another simple command
    - command: "enable" # interactive command
      then: ["{{ enable_password }}"]
    - command: "logout" # another interactive command
      then:
        - "y"
        - "n"

You can use the following keys for each command step:

Key Description Required Default Value
command Directly run the command on the device. Yes -
then Optional interactive input sequence to send after command. Must be a YAML list (then: ["value1", "value2", ...]) with up to 5 entries. Empty strings are sent as Enter. String values may use {{ enable_password }} to inject the device enable password. No -
description A brief description of the command. No -
metadata If set to true, the output of this command will be saved as comment-prefixed metadata block in the backup job log. This is useful for adding important information to the backup job log. No false
wait_for_prompt If set to false, KiwiSSH will not wait for the command prompt to return after running this command before proceeding to the next step. Use with caution. No true
show_command_in_config If set to true, the command will be included directly in the main config body above its output, prefixed with the comment prefix. This provides better context for the captured output when viewing the saved config file. show_command_in_config: true and metadata: true cannot be used together and will fail validation. No false

processing

Key Description Required Default Value
processing.strip_patterns A list of regex patterns. Lines matching any of these patterns will be removed from the captured config before saving. No -
processing.config_start A regex pattern that marks the start of the actual configuration in the CLI output. Lines before the matched line will be removed. No -
processing.config_end A regex pattern that marks the end of the actual configuration in the CLI output. Lines after the matched line will be removed. No -
processing.redaction.enabled If set to true, the patterns defined in processing.redaction.patterns will be applied to the captured config to redact sensitive information. No false
processing.redaction.patterns A list of redaction rules. Each rule should contain a search regex pattern, a replacement string, and an optional ignore_case boolean. No -

Important

If ignore_case is set to true for a redaction rule (default is false), the search pattern will be applied in a case-insensitive manner. Make sure to set this according to the expected CLI output of your devices to ensure effective redaction.

Processing behavior:

  • processing is optional; if no processing options are configured, config output is left unchanged
  • strip_patterns: remove matching lines
  • config_start / config_end: crop output to boundaries; if one side is missing, file edge is used
  • redaction.enabled + redaction.patterns: apply replacements
  • Each redaction rule uses search, replacement, and optional ignore_case

SSH Profiles YAML file

Found at /config/ssh_profiles.yaml

SSH profiles define reusable options for SSH connections. Assign a profile via groups.<group>.ssh_profile and optionally override it per device with nodes.<device_name>.ssh_profile.

Note

Notes for the usage of known_hosts_policy: strict validates against ~/.ssh/known_hosts ignore skips known-host validation (mapped to known_hosts: None in AsyncSSH) auto_add currently falls back to ignore and logs a warning Device and jumphost connections can use separate profiles (ssh_profile for devices, jumphost.ssh_profile for jumphosts) Port and timeout are configured in group/node/app settings, not in SSH profiles

You can create your own SSH profile by adding a new entry to the ssh_profiles.yaml file. Each profile should have a unique name.

FAQ

Frequently Asked Questions about the configuration of KiwiSSH.

How do I setup a remote git location?

KiwiSSH can push local git commits via SSH to their remote repository. To set up the remote repositories (in general), follow these steps:

  1. Create one remote repository per group.
    • If all groups live under one org, use one global template in git.remote.url with {group}.
    • If groups are spread across multiple organizations, set per-group overrides under groups.<group>.git.remote.url.
  2. Ensure the branch from git.remote.branch (or per-group branch override) exists or can be created by the push user. Default is main.
  3. Create an SSH keypair for KiwiSSH if you haven't already and add the public key to your Git provider.
  4. Make sure the remote Git user has write access to the repositories
  5. Run backup and confirm commits are present locally and remotely.

Tip

Since KiwiSSH will use the local OpenSSH client to push commits to the remote repository, the SSH config file (~/.ssh/config or C:\Users\user\.ssh\config) can be used to manage SSH connection details for the Git provider (e.g. GitHub, GitLab, etc.) and set up things like SSH key usage, custom ports, etc.

Example: groups.<group>.git.remote.url: "ssh://git@gitea/customer-aaa/prod-firewalls.git" Config in ~/.ssh/config:

Host gitea
   HostName 192.168.24.4
   Port 222
   User git
   IdentityFile ~/.ssh/kiwissh-client

When do I choose the global git.remote vs per-group overrides (groups.<group>.git.remote)?

Global: You should use the global git.remote configuration if all your groups should push to the same remote organization/repository structure (for example one repository per group under the same organization on GitHub). In this case, you can use the {group} placeholder in the global git.remote.url to dynamically generate the remote URL for each group based on its name. -> Example:git.remote.url: git@github.com:<YOUR_ORGANIZATION_HERE>/{group}.git will result in repositories like <YOUR_ORGANIZATION_HERE>/customer-aaa.git, <YOUR_ORGANIZATION_HERE>/abc-company.git, etc.

Group overrides: If your groups belong to different organizations or if you need more granular control over the remote URL each group, setup per-group overrides. In this case, you would leave the global git.remote configuration empty (aka remove it) and set the git.remote.url and optionally git.remote.branch for each group under groups.<group>.git.remote. -> Example: Group 1 groups.datacenter-firewalls.git.remote.url: ssh://git@192.168.45.25:222/company-abc/datacenter-firewalls.git, Group 2 groups.office-switches.git.remote.url: ssh://git@192.168.45.25:222/company-xyz/office-switches.git

In the next example, the global git.remote.url is configured with a placeholder {group} which will be replaced by the actual group name for each group. All groups will use the global template except for development-firewalls which has a per-group override for the remote URL, so it will push to the specified SSH URL instead of the global template.

git:
  local_path: "/config/prod/kiwissh/backups"
  remote:
    url: "ssh://git@github.com:<YOUR_ORGANIZATION_HERE>/kiwissh-{group}.git"

groups:
  development-firewalls:
    username: "admin"
    password: "password"
    vendor: "fortinet_fortigate"
    ssh_profile: "modern"
    git:
      remote:
        url: "ssh://git@github.com:dev_orga/development-firewalls.git"
        branch: "dev"

fatal: Could not read from remote repository

If you encounter the error message ..

ERROR: Remote push failed for group <your-group>: Cmd('git') failed due to: exit code(128)
  cmdline: git push --porcelain -- origin main:main
  stderr: 'fatal: Could not read from remote repository.'

.. you should do ssh git@<domain> manual first to add the host to ~/.ssh/known_hosts.

Tip

In order to persistently keep the known_hosts file in Docker, make sure to mount a volume to /home/kiwissh/.ssh and ensure the permissions are correct to ensure successful SSH authentication when pushing even after restarting the container.

Third Party Monitoring

You can monitor the status of your devices and backup jobs with third-party monitoring tools like Uptime Kuma, Icinga, Nagios, etc. by scraping the API endpoint /api/v1/devices/{device_name}. You can set up alerts based on the backup status (e.g. if the latest backup job failed or if no successful backup has been recorded for a certain period of time).

The endpoint will return the following information about the device:

Important

Response based on KiwiSSH Backend v2.4.0 (16.06.2026). Make sure to check the API documentation for the version you are using to confirm the response structure.

{
  "device_name": "hostname.domain.tld",
  "ip_address": "10.30.243.42",
  "vendor": "cisco_ios",
  "group": "customer-abc-router",
  "ssh_profile": "modern",
  "protocol": "telnet",
  "port": 23,
  "enabled": true,
  "status": "backup_no_changes",
  "last_backup": "2026-06-16T07:34:00.299083Z",
  "last_backup_success": "2026-06-16T07:34:00.299083Z",
  "last_error": null,
  "backup_count": 31,
  "schedule": "0 5,19 * * * (Europe/Berlin)"
}

Future Goals

Short Term:

  • Checks for device source: No duplicate hostnames, valid IPs, ... (What if multiple groups hold the same IP address range?)
  • Update ssh_service to use FQDN instead of IP to avoid?
  • Add new vendors
  • Cache backup history response for a device so it doesnt need to fetch again

Mid-term:

  • Add visual popup when opening JobView.vue if the page load takes longer than 2 seconds to inform the user that the page is still loading and to prevent them from thinking the UI is frozen (Branch job-log-loading-spinner)
  • Pentests
  • Rework Pydantic models (required vs optional fields, default values, validators, etc.)
  • Update Ruff linter and formatter
  • Add Vue linter/formatter?
  • SCP support?
  • Update ssh_profile legacy with Synology NAS ssh settings "Low"
  • Show "backup time" when NO_CHANGES" in log line "No configuration changes detected for {device_name}"?
  • Put redaction boolean switch as group/node level override instead of per vendor to be able to easily turn on/off redaction for specific groups/nodes without having to modify the vendor YAML files?
  • Full-text config search (#48)
  • Config file browser on the frontend (#47)

Long Term:

  • i18n localization support
  • Fix logging strings to use lazy formatting instead of f-strings (Ruff G004) (Add to ruff.toml)
  • Swagger API documentation on GitHub Pages
  • Allow group passwords to bet set via env vars or other input
  • Optional share anonymouse usage data for statistics (needs opt-in, privacy policy and telemetry server)
  • Login Screen, User management and RBAC
  • Allow merging of multiple device sources

Technical Documentation

Please visit TECHNICAL.md for detailed technical documentation about the architecture and design decisions of KiwiSSH.

Contributing to KiwiSSH

Important

If you would like to contribute to the project, please to a look at the CONTRIBUTING.md file for guidelines on how to contribute and the code of conduct.

License

See LICENSE file for license information.

Support

I work on KiwiSSH in my free time and unpaid. If you find it useful and would like to support its development, consider supporting me with a coffee:

Buy Me a Coffee

About

Backup your network device configurations with ease and keep track of changes. 4.52x faster than Oxidized!

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages