Skip to main content

Synchronize local project trees into running Kubernetes pods

Project description

sync2pod

sync2pod mirrors a local project tree into a running Kubernetes pod and then keeps it synchronized with bounded, archive-based updates. It is designed for local development workflows that already have a working kubectl command or a compatible wrapper.

This project is alpha software. Its configuration and command behavior may change.

Installation

Install from PyPI:

python -m pip install sync2pod

Build and install a local wheel from source:

./scripts/build-package.sh
python3 -m pip install dist/sync2pod-0.1.2-py3-none-any.whl

Python 3.10 through 3.13 are supported. The wheel installs two equivalent console commands:

sync2pod --help
sync_local_to_pod --help

The sync_local_to_pod name preserves compatibility with existing scripts and mtools integrations.

Quick start

Create a filesystem-backed project configuration, inspect it, and run a dry run before connecting to a pod:

sync2pod --init-config --local-path /path/to/project --project demo
$EDITOR ~/.sync2pod/demo/sync_config.json
sync2pod --list-projects
sync2pod --project demo --dry-run
sync2pod --project demo

Without --project, the project name defaults to the final component of --local-path during initialization.

Configuration

Each normal CLI project is stored as ~/.sync2pod/<project>/sync_config.json. Existing JSON project objects remain compatible with the standalone package. Unknown legacy fields are ignored.

A standard kubectl configuration looks like this:

{
  "cluster": "development",
  "namespace": "workspace",
  "pod_label": "app=my-service",
  "remote_parent_path": "/workspace",
  "local_path": "/Users/me/src/my-service",
  "container": "app",
  "kubectl_command": ["kubectl", "--context", "development"],
  "follow_sync": [],
  "exclude_paths": [".git", "node_modules", "*.log"],
  "max_workers": 10,
  "debounce_seconds": 1.0,
  "no_watch": false,
  "skip_verify": false,
  "prune": true,
  "upload_chunk_count": 6,
  "target_chunk_size_mb": 64
}

Wrapper commands are represented as an argument array, not a shell string. {cluster} is replaced with the configured cluster value:

{
  "cluster": "development",
  "kubectl_command": ["cluster-wrapper", "--target={cluster}", "kubectl"]
}

The complete schema is:

Field Required Default Meaning
cluster yes - Cluster identifier available to wrapper commands.
namespace yes - Namespace containing the target pod.
pod_label yes - Label selector used to choose the first running pod.
remote_parent_path yes - Absolute POSIX parent; the local directory name is appended.
local_path yes - Existing primary source directory.
container no null Explicit target container for every exec and cp; omit it for kubectl's default container.
kubectl_command no ["kubectl"] Non-empty argument array used before every Kubernetes operation.
follow_sync no [] Additional source directories mapped below the remote project.
exclude_paths no [] Path-name and glob exclusions applied to full and sparse sync.
compress_threshold no 100 Positive legacy-compatible tuning value.
max_workers no 10 Maximum sparse-watch worker count.
debug no false Legacy-compatible diagnostic setting.
show_concurrency no false Legacy-compatible concurrency display setting.
no_watch no false Exit after the startup full mirror.
skip_verify no false Skip the interactive configuration confirmation.
debounce_seconds no 1.0 Positive watch-event debounce interval.
prune no true Legacy-compatible setting; startup remains an authoritative mirror.
upload_chunk_count no 6 Upper bound used when scheduling archive shards.
target_chunk_size_mb no 64 Positive target size for startup archive shards.

cluster, namespace, pod_label, remote_parent_path, and local_path must be present and non-empty. Boolean and positive-number fields are validated strictly.

Follow mappings and managed symlinks

follow_sync maps directories outside the primary tree into named children of the remote project:

{
  "follow_sync": [
    {
      "local_path": "/Users/me/src/shared-library",
      "alias": "shared",
      "create_symlink": true
    }
  ]
}

The remote mapping is /workspace/my-service/shared in this example. When alias is omitted or blank, the follow directory name is used. Aliases must be single safe path segments and must be unique.

With create_symlink: true, sync2pod also manages <local_path>/shared -> /Users/me/src/shared-library before synchronization. It reuses an exact existing symlink but refuses to overwrite a file, directory, or conflicting symlink. Set create_symlink to false to keep only the remote mapping.

Synchronization behavior

Every normal start performs an authoritative compressed full mirror before watch mode becomes active. Files are archived locally, copied in a bounded number of shards, verified, extracted into staging directories, and switched into place only after all mappings are ready.

Warning: the remote project path is a mirror target, not a merge destination. Startup replacement and later delete events remove remote content that is not present locally. Do not point remote_parent_path/local-directory-name at valuable pod data.

While watching, sync2pod batches filesystem events and applies sparse file, subtree, and deletion actions. Independent work can run concurrently up to max_workers; conflicting ancestor and descendant operations remain ordered. The selected pod is fixed for the session, so restart the command after the pod is replaced.

Unconfigured symlinks are archived as symlinks and are never followed outside the configured roots. Sparse updates reject symlink paths that could escape a source directory. Device files and other unsupported special files are not a supported synchronization mechanism.

Command-line interface

Both console names accept the same legacy-compatible flags:

Flag Behavior
--init-config Create a project configuration. Requires --local-path.
--list-projects List configured projects.
--project NAME Select the project configuration.
--local-path PATH Source directory used during initialization.
--force Accepted for compatibility; startup already performs a full mirror.
--skip-verify Skip the interactive confirmation for this invocation.
--dry-run Build local archives and report mappings, counts, bytes, shards, and target without pod operations or symlink mutation.

For unattended runs, set skip_verify in the project configuration or pass --skip-verify.

Pod requirements

The local machine must provide the configured Kubernetes command and permission to list pods, execute commands, and copy files in the selected namespace. The target container must provide:

sh bash find tar md5sum rm mkdir mv cat

The process also needs write permission for the remote parent, staging paths, and final project path.

mtools and embedded backends

mtools can keep project JSON in its own database instead of ~/.sync2pod. It launches the compatibility command with:

SYNC2POD_CONFIG_BACKEND=utools-db
SYNC2POD_CONFIGS_JSON={"project-name": { ... project configuration ... }}

When this backend saves configuration, stdout contains one machine-readable line prefixed with __SYNC2POD_DB_UPDATE__ . Watch progress is emitted on stderr with __SYNC2POD_PROGRESS__ and the JSON keys pending, uploading, session_done, and session_failed. Embedders should consume these control lines and keep them out of user-facing logs.

The public Python entry point is also available:

from sync2pod import run

run(project_configuration)

Safety boundaries and non-goals

  • sync2pod synchronizes into one already-running pod; it does not deploy, restart, or supervise workloads.
  • It does not provide bidirectional synchronization, conflict resolution, or a durable cross-machine journal.
  • It does not automatically reselect pods during a watch session.
  • It does not interpret shell strings in kubectl_command.
  • It does not promise preservation of remote-only files inside the mirror target.

Troubleshooting

  • No running pod found: verify namespace, pod_label, and the configured command by running its equivalent get pods request manually.
  • Missing required commands: add the named tools to the target container; minimal images often omit bash, tar, or md5sum.
  • Permission denied: verify local read access and remote write access, including the configured parent directory.
  • Configuration confirmation blocks automation: pass --skip-verify or set skip_verify to true.
  • Follow symlink conflict: remove or rename the conflicting local path, or set create_symlink to false for that follow mapping.
  • Pod restarted: stop and relaunch sync2pod so it selects the replacement running pod and performs a fresh authoritative mirror.

Development

uv sync --all-groups
uv run pytest -m "not performance" --cov=sync2pod --cov-report=term-missing
uv run pytest -m performance
uv build

License

Licensed under the Apache License 2.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sync2pod-0.1.2.tar.gz (85.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sync2pod-0.1.2-py3-none-any.whl (37.1 kB view details)

Uploaded Python 3

File details

Details for the file sync2pod-0.1.2.tar.gz.

File metadata

  • Download URL: sync2pod-0.1.2.tar.gz
  • Upload date:
  • Size: 85.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for sync2pod-0.1.2.tar.gz
Algorithm Hash digest
SHA256 394b34371c8d297ed7caa0a04579f5f3e5286ec0bea01c4126b8138a85d05f29
MD5 85204e0853ee188e43a6f38063d215fc
BLAKE2b-256 f1b6f57c587aaab423ea1f76639e4d6ac64abfc76f3a2e8d8e959f77ec3fd4e0

See more details on using hashes here.

File details

Details for the file sync2pod-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: sync2pod-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 37.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for sync2pod-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 dd0a14029a7af4b90e106a9f7d1c189c1453c138bc4b6e5e09cfe2b93b28bf0a
MD5 e9dc2c80a67aea2e2c0ab63e31ff5688
BLAKE2b-256 6d5fe4e4a94cbd652c9c20f4ad379b260517abc0262dbdf60febaf82529a1919

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page