{"id":44081077,"url":"https://github.com/anuragxxd/conops","last_synced_at":"2026-02-17T15:03:13.122Z","repository":{"id":337023478,"uuid":"1146346814","full_name":"anuragxxd/conops","owner":"anuragxxd","description":"GitOps controller for Docker Compose - Self-healing, Git-driven deployments without Kubernetes","archived":false,"fork":false,"pushed_at":"2026-02-15T20:55:35.000Z","size":20589,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-02-15T23:35:11.185Z","etag":null,"topics":["argocd-alternative","continuous-deployment","devops","docker-compose","gitops","golang","homelab","self-hosted"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anuragxxd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-31T00:28:37.000Z","updated_at":"2026-02-15T20:53:45.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/anuragxxd/conops","commit_stats":null,"previous_names":["anuragxxd/conops"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/anuragxxd/conops","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anuragxxd%2Fconops","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anuragxxd%2Fconops/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anuragxxd%2Fconops/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anuragxxd%2Fconops/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anuragxxd","download_url":"https://codeload.github.com/anuragxxd/conops/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anuragxxd%2Fconops/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29548201,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T14:33:00.708Z","status":"ssl_error","status_checked_at":"2026-02-17T14:32:58.657Z","response_time":100,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["argocd-alternative","continuous-deployment","devops","docker-compose","gitops","golang","homelab","self-hosted"],"created_at":"2026-02-08T09:10:23.034Z","updated_at":"2026-02-17T15:03:13.117Z","avatar_url":"https://github.com/anuragxxd.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  \u003cimg src=\"docs/media/conops.png\" alt=\"ConOps Logo\" width=\"200\"\u003e\n  \u003cbr\u003e\n  ConOps\n\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eGitOps for Docker Compose. Like Argo CD, but without Kubernetes.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/anuragxxd/conops/stargazers\"\u003e\u003cimg src=\"https://img.shields.io/github/stars/anuragxxd/conops?style=flat-square\" alt=\"GitHub stars\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://hub.docker.com/r/anurag1201/conops\"\u003e\u003cimg src=\"https://img.shields.io/docker/pulls/anurag1201/conops?style=flat-square\" alt=\"Docker Pulls\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/anuragxxd/conops/blob/master/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/anuragxxd/conops?style=flat-square\" alt=\"License\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/anuragxxd/conops/releases\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/anuragxxd/conops?style=flat-square\" alt=\"Release\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  Point ConOps at a Git repo containing a \u003ccode\u003edocker-compose.yaml\u003c/code\u003e.\u003cbr\u003e\n  It clones, pulls, deploys, watches for new commits, detects container drift, and self-heals.\u003cbr\u003e\n  One binary. No cluster required.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"#quick-start\"\u003eQuick Start\u003c/a\u003e \u0026middot;\n  \u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e \u0026middot;\n  \u003ca href=\"#usage-guide\"\u003eUsage Guide\u003c/a\u003e \u0026middot;\n  \u003ca href=\"#configuration\"\u003eConfiguration\u003c/a\u003e \u0026middot;\n  \u003ca href=\"#method-2-rest-api--cli\"\u003eAPI\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n![ConOps Demo](docs/media/conops-demo.gif)\n\n## Quick Start\n\nRun ConOps with a single command:\n\n```bash\ndocker run \\\n  --name conops \\\n  -p 8080:8080 \\\n  -e CONOPS_RUNTIME_DIR=/tmp/conops-runtime \\\n  -v /tmp/conops-runtime:/tmp/conops-runtime \\\n  -v conops_data:/data \\\n  -v /var/run/docker.sock:/var/run/docker.sock \\\n  anurag1201/conops:latest\n```\n\nOpen **http://localhost:8080** and click **New App**.\n\n\u003e **Note:** For production use, we recommended using [Docker Compose](#production-setup).\n\n## Features\n\n- **Git-driven deployments** \u0026mdash; push to your branch, ConOps handles the rest\n- **Continuous reconciliation** \u0026mdash; configurable loop that keeps desired state in sync\n- **Self-healing** \u0026mdash; detects missing, exited, or unhealthy containers and recovers\n- **Docker preflight + fallback toolchain** \u0026mdash; checks Docker API compatibility before sync\n- **Web UI** \u0026mdash; register apps, inspect status, view containers, trigger syncs, read logs\n- **REST API + CLI** \u0026mdash; automate everything; nothing in the UI that the API can't do\n- **Private repo support** \u0026mdash; GitHub deploy keys with AES-GCM encryption at rest\n- **SQLite or PostgreSQL** \u0026mdash; SQLite for single-node, Postgres for production\n- **Single binary** \u0026mdash; no runtime dependencies beyond Docker and Git\n\n## Usage Guide\n\nConOps can be managed in two ways: via the **Web Dashboard** or the **REST API / CLI**.\n\n### Method 1: Web Dashboard\n\nThe easiest way to get started. Navigate to `http://localhost:8080`.\n\n*   **Dashboard**: View all registered applications and their current status (`synced`, `syncing`, `pending`, `error`).\n*   **New App**: Click the button to register a repository. You'll need the Git URL, branch name, and path to the Compose file.\n*   **App Details**: Click on any app to see its sync history, container health, and logs.\n*   **Actions**: You can manually trigger a sync or delete an app directly from its card.\n\n### Method 2: REST API \u0026 CLI\n\nConOps is API-first. Every action available in the UI can be performed via the REST API.\n\n#### CLI Tool (`conops-ctl`)\n\nThe `conops-ctl` CLI helps you manage applications from your terminal.\n\n**Installation:**\n\n```bash\n# Install the latest version to /usr/local/bin\ncurl -sfL https://raw.githubusercontent.com/anuragxxd/conops/master/install.sh | sudo sh\n```\n\nOr download pre-built binaries from the [Releases](https://github.com/anuragxxd/conops/releases) page.\n\n**Configuration:**\nSet the controller URL if running remotely (defaults to `http://localhost:8080`):\n```bash\nexport CONOPS_URL=\"http://conops.example.com:8080\"\n```\n\n**Commands:**\n```bash\n# List all apps\n./conops-ctl apps list\n\n# Register an app (supports private repos via JSON)\n./conops-ctl apps add app.json\n\n# Get detailed status\n./conops-ctl apps get \u003capp-id\u003e\n\n# Update configuration (partial updates supported)\n./conops-ctl apps update \u003capp-id\u003e --branch feature/login --poll-interval 1m\n\n# Force immediate sync\n./conops-ctl apps sync \u003capp-id\u003e\n```\n\n#### REST API\n\nYou can interact directly with the API using `curl`.\n\n**1. Register an App**\n```bash\ncurl -X POST http://localhost:8080/api/v1/apps/ \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Example App\",\n    \"repo_url\": \"https://github.com/docker/awesome-compose\",\n    \"repo_auth_method\": \"public\",\n    \"branch\": \"master\",\n    \"compose_path\": \"fastapi/compose.yaml\",\n    \"poll_interval\": \"30s\"\n  }'\n```\n\n**2. List Apps**\n```bash\ncurl http://localhost:8080/api/v1/apps/\n```\n\n**3. Get App Details**\n```bash\ncurl http://localhost:8080/api/v1/apps/{id}\n```\n\n**4. Force Sync**\n```bash\ncurl -X POST http://localhost:8080/api/v1/apps/{id}/sync\n```\n\n**5. Update App**\n```bash\ncurl -X PATCH http://localhost:8080/api/v1/apps/{id} \\\n  -H \"Content-Type: application/json\" \\\n  -d '{ \"poll_interval\": \"1m\" }'\n```\n\n**6. Delete App**\n```bash\ncurl -X DELETE http://localhost:8080/api/v1/apps/{id}\n```\n\n## Configuration\n\nAll configuration is via environment variables.\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `DB_TYPE` | `sqlite` | Storage backend: `sqlite` or `postgres` |\n| `DB_CONNECTION_STRING` | \u0026mdash; | Required when using `postgres` |\n| `CONOPS_RECONCILE_INTERVAL` | `10s` | How often the reconciler runs |\n| `CONOPS_SYNC_TIMEOUT` | `5m` | Max duration for a single sync operation |\n| `CONOPS_RETRY_ERRORS` | `false` | Auto-retry apps that entered `error` status |\n| `CONOPS_RUNTIME_DIR` | `./.conops-runtime` | Runtime checkout directory used for compose execution |\n| `CONOPS_TOOLS_DIR` | `./.conops-tools` | Cache directory for managed Docker CLI and Compose plugin downloads |\n| `CONOPS_ENCRYPTION_KEY` | \u0026mdash; | 32-byte key (raw or base64) for deploy key encryption |\n| `CONOPS_ENCRYPTION_KEY_FILE` | `/data/conops-encryption.key` | Path to read/write the encryption key |\n\n## Production Setup\n\nFor production, we recommend running ConOps with Docker Compose to handle persistence and networking cleanly.\n\n```yaml\nservices:\n  conops:\n    image: anurag1201/conops:latest\n    ports:\n      - \"8080:8080\"\n    environment:\n      - CONOPS_RUNTIME_DIR=/tmp/conops-runtime\n      - DB_TYPE=sqlite\n    volumes:\n      # Persist SQLite DB and encryption keys\n      - conops_data:/data\n      # Allow ConOps to manage sibling containers\n      - /var/run/docker.sock:/var/run/docker.sock\n      # Runtime directory for checkouts\n      - /tmp/conops-runtime:/tmp/conops-runtime\n\nvolumes:\n  conops_data:\n```\n\n## Private Repositories\n\nConOps supports private GitHub/GitLab repositories via SSH deploy keys.\n\nWhen registering an app via the API, set `repo_auth_method` to `deploy_key` and provide the private key:\n\n```bash\ncurl -X POST http://localhost:8080/api/v1/apps/ \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"Private App\",\n    \"repo_url\": \"git@github.com:my-org/private-repo.git\",\n    \"repo_auth_method\": \"deploy_key\",\n    \"deploy_key\": \"-----BEGIN OPENSSH PRIVATE KEY-----\\n...\\n-----END OPENSSH PRIVATE KEY-----\",\n    \"branch\": \"main\",\n    \"compose_path\": \"docker-compose.yml\"\n  }'\n```\n\n\u003e **Security:** Deploy keys are encrypted at rest using AES-GCM. ConOps auto-generates an encryption key on first run, or you can provide your own via `CONOPS_ENCRYPTION_KEY`.\n\n## How It Works\n\n```\n                    ┌─────────────┐\n                    │  Git Repo   │\n                    └──────┬──────┘\n                           │ poll\n                    ┌──────▼──────┐\n                    │ Git Watcher │──── new commit? ──→ mark pending\n                    └─────────────┘\n                           │\n                    ┌──────▼──────┐\n                    │ Reconciler  │──── clone/fetch → compose pull → compose up\n                    └──────┬──────┘\n                           │\n                    ┌──────▼──────┐\n                    │Drift Checker│──── container missing/exited/unhealthy? → requeue\n                    └─────────────┘\n```\n\n**Status flow:** `registered` \u0026rarr; `pending` \u0026rarr; `syncing` \u0026rarr; `synced`.\n\nConOps separates **change detection** (Git watcher) from **state application** (reconciler). This keeps the control loop predictable and easy to reason about.\n\n## Development\n\n```bash\n# Run the controller locally\ngo run ./cmd/conops\n\n# Build the CLI\ngo build -o bin/conops-ctl ./cmd/conops-ctl\n\n# Run tests\ngo test ./...\n```\n\n## License\n\nMIT \u0026mdash; see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanuragxxd%2Fconops","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanuragxxd%2Fconops","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanuragxxd%2Fconops/lists"}