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 equivalentget podsrequest manually. - Missing required commands: add the named tools to the target container;
minimal images often omit
bash,tar, ormd5sum. - Permission denied: verify local read access and remote write access, including the configured parent directory.
- Configuration confirmation blocks automation: pass
--skip-verifyor setskip_verifytotrue. - Follow symlink conflict: remove or rename the conflicting local path, or
set
create_symlinktofalsefor 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
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
394b34371c8d297ed7caa0a04579f5f3e5286ec0bea01c4126b8138a85d05f29
|
|
| MD5 |
85204e0853ee188e43a6f38063d215fc
|
|
| BLAKE2b-256 |
f1b6f57c587aaab423ea1f76639e4d6ac64abfc76f3a2e8d8e959f77ec3fd4e0
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dd0a14029a7af4b90e106a9f7d1c189c1453c138bc4b6e5e09cfe2b93b28bf0a
|
|
| MD5 |
e9dc2c80a67aea2e2c0ab63e31ff5688
|
|
| BLAKE2b-256 |
6d5fe4e4a94cbd652c9c20f4ad379b260517abc0262dbdf60febaf82529a1919
|