A simple, no-nonsense deployment tool for Go applications on Linux servers with systemd
launchd is a lightweight CI/CD helper that deploys Go binaries to Linux servers from your laptop. No Docker, no complex pipelinesβjust simple, effective deployments using SSH and systemd.
- π¨ Build your Go app locally
- π¦ Copy the binary to your server over SSH
- βοΈ Create/refresh a systemd service
- ποΈ Run database migrations (optional)
- β Verify health before finishing
Perfect for side projects, MVPs, and small-scale deployments where simplicity matters.
Before you start, make sure you have:
- β Go installed (1.21+)
- β OpenSSH client (
ssh,scp) - β A Go project with a
mainpackage
- β Linux with systemd (Ubuntu, Debian, CentOS, Fedora, etc.)
- β SSH access with sudo privileges
- β Network port open (e.g., 8080)
- β Health endpoint (e.g.,
GET /healthβ200 OK) - β Configurable port (via flag or environment variable)
git clone https://github.com/yourusername/launchd.git
cd launchdgo build -o launchd ./cmd/launchdsudo mv launchd /usr/local/bin/Verify installation:
launchd --versionEnsure your app builds locally:
go build -o ./bin/myapp ./path/to/your/cmdMake sure it:
- Accepts a port configuration (flag or env var)
- Exposes a
/healthendpoint that returns200 OK
launchd deploy \
--host your-server.com \
--user deploy \
--app ./bin/myapp \
--port 8080 \
--timeout 60sβββββββββββββββ
β 1. BUILD β Compile Go binary locally
ββββββββ¬βββββββ
β
ββββββββΌβββββββ
β 2. TRANSFER β SCP binary to server
ββββββββ¬βββββββ
β
ββββββββΌβββββββ
β 3. SERVICE β Create/update systemd unit
ββββββββ¬βββββββ
β
ββββββββΌβββββββ
β 4. MIGRATE β Run DB migrations (optional)
ββββββββ¬βββββββ
β
ββββββββΌβββββββ
β 5. VERIFY β Health check polling
βββββββββββββββ
- Build: Uses your local Go toolchain to compile the binary
- Transfer: Copies via
scpto/usr/local/bin/<app> - Service: Generates systemd unit file and runs:
systemctl daemon-reloadsystemctl enable <app>systemctl restart <app>
- Migrate: Executes migration tool if configured
- Health Check: Polls
http://<host>:<port>/healthuntil success
launchd deploy --host <server> --user <username> --app <binary> --port <port>launchd deploy \
--host server.com \
--user deploy \
--app ./bin/myapp \
--port 8080 \
--migrate \
--migration-cmd "migrate -path /path/to/migrations -database postgres://..."launchd deploy \
--host server.com \
--user deploy \
--app ./bin/myapp \
--port 8080 \
--health-path /api/health# Test SSH access first
ssh deploy@your-server.comSolutions:
- Verify SSH keys are set up:
ssh-copy-id deploy@your-server.com - Check firewall rules allow SSH (port 22)
Problem: User cannot run systemctl without password
Solution: Add sudo permissions for systemctl:
# On the server, edit sudoers
sudo visudo
# Add this line (replace 'deploy' with your username)
deploy ALL=(ALL) NOPASSWD: /bin/systemctl, /usr/bin/install# Check what's using the port
sudo lsof -i :8080
# Stop the conflicting service
sudo systemctl stop conflicting-servicePossible causes:
- App takes too long to start (increase
--timeout) - Wrong port or health endpoint path
- App crashes on startup
Check logs:
ssh deploy@server.com 'sudo journalctl -u myapp.service -f'# Check if service is running
ssh deploy@server.com 'sudo systemctl status myapp.service'
# View recent logs
ssh deploy@server.com 'sudo journalctl -u myapp.service --since "10 minutes ago"'ssh user@server 'sudo systemctl status myapp.service'# Follow logs in real-time
ssh user@server 'sudo journalctl -u myapp.service -f'
# Last 100 lines
ssh user@server 'sudo journalctl -u myapp.service -n 100'ssh user@server 'sudo systemctl restart myapp.service'ssh user@server 'sudo systemctl stop myapp.service'ssh user@server << 'EOF'
sudo systemctl stop myapp.service
sudo systemctl disable myapp.service
sudo rm -f /usr/local/bin/myapp
sudo rm -f /etc/systemd/system/myapp.service
sudo systemctl daemon-reload
EOFGood news: launchd is idempotent! You can run the same deploy command multiple times safely.
# Make changes to your code
# Then deploy again
launchd deploy --host server.com --user deploy --app ./bin/myapp --port 8080What happens:
- β Binary is overwritten with new version
- β Service is restarted with zero-downtime goal
- β Health check confirms new version is running
Do I need Docker?
No! launchd deploys native binaries directly. No containers required.
Do I need Go installed on the server?
No. Building happens locally on your machine. The server only runs the pre-compiled binary.
Do I need root access?
You need a user with sudo privileges to:
- Copy files to
/usr/local/bin - Manage systemd services
You don't need to log in as root directly.
Can I deploy to multiple servers?
Yes! Run the deploy command once per server, or write a simple bash loop:
for server in server1.com server2.com server3.com; do
launchd deploy --host $server --user deploy --app ./bin/myapp --port 8080
doneWhat about environment variables?
Add them to your systemd service file or use a tool like envdir. Future versions of launchd may support this natively.
Is this production-ready?
launchd works great for:
- β Side projects
- β MVPs and prototypes
- β Internal tools
- β Small-scale production apps
For large-scale production with complex requirements, consider Kubernetes, Nomad, or managed platforms.
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Built with β€οΈ for developers who prefer simplicity over complexity.
Why launchd? Because sometimes you just want to ship code, not configure orchestrators.
Documentation β’ Installation β’ Quick Start β’ Troubleshooting
Made with Go β’ Deployed with launchd