A single interactive shell script that clones your repository onto a server, detects its stack, builds and runs it, sets up a Caddy reverse proxy, and generates a ready-to-commit CI pipeline file (GitHub Actions or GitLab CI).
- Linux server (Debian/Ubuntu or RHEL/Fedora/CentOS)
- Run as root (needs to write to
/srv, install packages, and create systemd services) gitandcurlmust be installed
curl -fsSL https://github.com/M2KX/CISH/releases/latest/download/cish.sh -o cish.sh
chmod +x cish.sh
sudo bash cish.shDetects the package manager (apt, dnf, or yum) once at startup and uses it throughout. Installs openssh-client if not present.
Choose where your repository is hosted:
- GitHub
- GitLab (including self-hosted)
Provide the HTTPS clone URL. The repo name is extracted automatically.
If the repo is private, enter a Personal Access Token (PAT) or deploy token.
- GitLab: token is injected as
oauth2:TOKEN@host - GitHub: token is injected directly into the URL
The token is tested immediately with git ls-remote before anything is cloned. If it fails, the script exits with a clear error.
Enter the server's hostname/IP, SSH username, and SSH port. These are used later to generate the CI secrets table.
The repository is cloned into:
/srv/<reponame>/repo/
If the directory already exists you are asked whether to re-clone or keep it.
If .env.example exists in the repo it is copied to:
/srv/<reponame>/.env
You are offered the option to open it in your editor immediately. If no .env.example is found an empty .env is created.
The script detects your project type in this order:
| Indicator | Strategy |
|---|---|
docker-compose.yml / docker-compose.yaml / compose.yaml |
docker-compose |
Dockerfile |
dockerfile |
*.csproj / *.sln |
dotnet |
package.json |
node |
go.mod |
go |
| None of the above | manual pick |
If a .dockerignore is present but a native strategy is chosen, a warning is shown.
For .NET projects you are asked for the folder containing the .csproj or .sln file. You can enter either the folder path or the file path directly — the script resolves it to the correct directory.
For native strategies (Node, Go, .NET) the required runtime is installed automatically if not already present:
| Runtime | Primary source | Fallback 1 | Fallback 2 |
|---|---|---|---|
| Node.js | NodeSource LTS repo | — | — |
| Go | Official tarball (go.dev) | snap | apt/dnf golang package |
| .NET SDK | Microsoft dotnet-install.sh |
— | — |
The application is built and started immediately after cloning (no git pull — we just cloned it).
| Strategy | What runs |
|---|---|
| docker-compose | docker compose up -d --build |
| dockerfile | docker build → docker run --restart unless-stopped |
| node | npm ci / npm install → npm run build → systemd service |
| go | go build → systemd service |
| dotnet | dotnet restore → dotnet build → dotnet publish → systemd service |
For native strategies (node/go/dotnet) a systemd service is created, enabled, and started:
- Service name: same as the repository name
- Auto-starts on reboot
- Restarts automatically on crash (
Restart=always) - Logs available via
journalctl -u <name> -f
A deploy.sh script is also written to /srv/<reponame>/deploy.sh. This script is what CI calls on every push — it pulls the latest code and restarts the service.
If you choose to set up Caddy:
- Caddy is installed via the official repository for your OS
- You provide a domain name
- A site config is written to
/etc/caddy/conf.d/<reponame>.caddy - Caddy is enabled and reloaded
example.com {
reverse_proxy localhost:<port>
}
HTTPS is handled automatically via Let's Encrypt. Make sure your DNS A record points to the server before the first request.
An ed25519 key pair is generated at:
/srv/<reponame>/ci_deploy_key ← private key (add as CI secret)
/srv/<reponame>/ci_deploy_key.pub ← public key (added to authorized_keys)
The public key is added to ~/.ssh/authorized_keys automatically.
A ready-to-commit CI file is generated inside the cloned repo:
| Platform | File |
|---|---|
| GitHub | .github/workflows/deploy.yml |
| GitLab | .gitlab-ci.yml |
The pipeline SSHs into the server and runs deploy.sh on every push to your chosen branch.
The script prints everything you need to finish the setup:
- Git commands to commit and push the CI file
- A table of CI secrets/variables with their exact values pre-filled
- The full private key to paste as
SSH_PRIVATE_KEY - For private repos: a reminder to add the token as
DEPLOY_TOKEN
/srv/<reponame>/
├── repo/ ← git clone
├── .env ← environment variables
├── deploy.sh ← called by CI on every push
├── ci_deploy_key ← SSH private key (add as CI secret)
└── ci_deploy_key.pub ← SSH public key (already in authorized_keys)
For native strategies, additional files appear:
/srv/<reponame>/
├── app ← Go binary
├── publish/ ← .NET publish output
└── app.log ← stdout/stderr (if not using journald)
| Variable | Value | Required for |
|---|---|---|
SERVER_HOST |
Server IP or hostname | All |
SERVER_USER |
SSH username | All |
SSH_PORT |
SSH port (default 22) | All |
SSH_PRIVATE_KEY |
Contents of ci_deploy_key |
All |
DEPLOY_TOKEN |
PAT / deploy token | Private repos only |
You can safely re-run cish.sh on the same server. It will:
- Ask whether to re-clone or keep the existing directory
- Skip
.envcreation if the file already exists - Skip SSH key generation if the key pair already exists
- Overwrite the CI file with the latest generated version
| Stack | Runtime install | Process manager |
|---|---|---|
| Docker Compose | — | Docker |
| Dockerfile | — | Docker |
| Node.js | NodeSource LTS | systemd |
| Go | go.dev tarball / snap | systemd |
| .NET | Microsoft dotnet-install.sh | systemd |
| Host | CI |
|---|---|
| Debian / Ubuntu | GitHub Actions |
| RHEL / Fedora / CentOS | GitLab CI |
MIT — see LICENSE
Made by M2KX