${sub}
${repo}
${host}
${src}
${status==='live'?'● LIVE':'○ PENDING'}
Install Git + GitHub CLI on your Windows machine
Download Git from git-scm.com. Install with default options. Then install GitHub CLI from cli.github.com.
# Verify installation git --version gh --version # Authenticate GitHub CLI gh auth login # Select: GitHub.com → HTTPS → Login with browser
Configure Git global identity
git config --global user.name "Sandip Shinde" git config --global user.email "shindesandip2022@gmail.com" git config --global core.autocrlf false
Cloudflare — Verify labelnex.in DNS zone
Log in to dash.cloudflare.com → labelnex.in → DNS. Ensure nameservers are active (check email from Cloudflare confirming activation). This is required before any CF Pages subdomain will resolve.
This is the standard pattern for all ZIP-based deployments. Follow this exactly for docs, admin2, portal, studio, admin.
Extract ZIP to a local folder
# Open PowerShell in your build folder cd "C:\Users\Admin\Documents\Startup SS Prep1\may-2026-current-build\deploy" # Unzip docs Expand-Archive -Path "docs.labelnex.in-v2.zip" -DestinationPath ".\docs-deploy" -Force cd docs-deploy
Create GitHub repo and push
# Initialise git repo git init git add . git commit -m "docs.labelnex.in v2 - initial deploy" # Create GitHub repo and push (gh CLI does this in one command) gh repo create shindesandip22/labelnex-docs --public --source=. --push # Verify gh repo view shindesandip22/labelnex-docs --web
Connect to Cloudflare Pages
Go to dash.cloudflare.com → Workers & Pages → Create application → Pages → Connect to Git
→ Select GitHub → Authorize Cloudflare → Select repo shindesandip22/labelnex-docs
→ Build settings: Framework preset: None | Build command: leave blank | Build output directory: / (or the folder with index.html)
→ Click Save and Deploy
Add custom domain docs.labelnex.in
In the Cloudflare Pages project → Custom domains → Set up a custom domain → Type docs.labelnex.in → Continue
Cloudflare will automatically add the CNAME record to your DNS. Wait 2–5 minutes for propagation.
https://docs.labelnex.in in browser. Should load your docs site with SSL.For portal.labelnex.in, studio.labelnex.in, and admin.labelnex.in — the source is already in your local sources/ folder.
# Navigate to the portal source folder cd "C:\Users\Admin\Documents\Startup SS Prep1\may-2026-current-build\sources\phase2-connected-surfaces-2026-04-29\labelnex-portal" # Initialise, commit, create repo and push git init git add . git commit -m "portal.labelnex.in - phase 2 connected surfaces" gh repo create shindesandip22/labelnex-portal --public --source=. --push # Repeat for studio cd ..\labelnex-studio git init && git add . && git commit -m "studio.labelnex.in initial" gh repo create shindesandip22/labelnex-studio --public --source=. --push # Repeat for admin cd ..\labelnex-admin git init && git add . && git commit -m "admin.labelnex.in initial" gh repo create shindesandip22/labelnex-admin --public --source=. --push
After pushing each repo, connect it to Cloudflare Pages following the same steps in Part 2, Steps 3–4. Each gets its own custom domain.
Set up SSH config on your Windows machine
# In PowerShell — generate SSH key if you don't have one ssh-keygen -t ed25519 -C "sandip@labelnex-hetzner" # Press Enter 3 times (accept defaults, no passphrase for automation) # Add SSH config entry notepad $HOME\.ssh\config # Paste this into the config file: Host labelnex-hetzner HostName YOUR_HETZNER_IP Port 22 User root IdentityFile ~/.ssh/id_ed25519
YOUR_HETZNER_IP with your actual Hetzner server IP from the Hetzner console. After running secure-hetzner.sh, change Port to 2222.Copy your public key to Hetzner (first-time setup)
# In Hetzner console → your server → Rescue → Add your public key # OR if you can already SSH as root: cat $HOME\.ssh\id_ed25519.pub | ssh labelnex-hetzner "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
Upload ZIP files and run deploy scripts
# Copy deploy files to Hetzner (from your Windows build folder) scp "labelnex-api-foundation-2026-05-06.zip" labelnex-hetzner:/tmp/ scp "labelnex-kb-portal-2026-05-06.zip" labelnex-hetzner:/tmp/ scp deploy-api-hetzner.sh labelnex-hetzner:/tmp/ scp deploy-kb.sh labelnex-hetzner:/tmp/ scp secure-hetzner.sh labelnex-hetzner:/tmp/ # SSH into Hetzner and run in order ssh labelnex-hetzner # On the server: cd /tmp chmod +x *.sh # 1. Security first (do this BEFORE deploy) bash secure-hetzner.sh # IMMEDIATELY update SSH config on Windows to use port 2222 after this! # 2. Deploy API bash deploy-api-hetzner.sh # 3. Edit .env with real passwords nano /home/labelnex/.env # Update: DB_PASSWORD, ANTHROPIC_API_KEY, BREVO_API_KEY # 4. Start API with PM2 sudo -u labelnex pm2 start /home/labelnex/ecosystem.config.js sudo -u labelnex pm2 save sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u labelnex --hp /home/labelnex # 5. Deploy KB portal bash deploy-kb.sh # 6. Add KB to ecosystem.config.js (as instructed in deploy-kb.sh output) nano /home/labelnex/ecosystem.config.js # Add labelnex-kb app block to the apps array sudo -u labelnex pm2 restart all sudo -u labelnex pm2 save # 7. Validate curl https://api.labelnex.in/health curl https://kb.labelnex.in/
All Cloudflare Pages subdomains are added automatically when you add a custom domain in Cloudflare Pages. But if you need to add manually:
# All Cloudflare Pages subdomains get a CNAME record: # TYPE NAME TARGET CNAME docs YOUR-PROJECT.pages.dev CNAME admin2 YOUR-PROJECT.pages.dev CNAME portal YOUR-PROJECT.pages.dev CNAME studio YOUR-PROJECT.pages.dev CNAME admin YOUR-PROJECT.pages.dev # Hetzner-hosted subdomains get A records: A api YOUR_HETZNER_IP A kb YOUR_HETZNER_IP # These should already exist (from existing deployments): A/CNAME @ (labelnex.in homepage - CF Pages) CNAME tools (tools.labelnex.in - CF Pages) A sandipshinde.com (CF Pages or separate)
After initial setup, every git push to main should automatically redeploy. Here's the setup:
For Cloudflare Pages repos (docs, admin2, portal, studio, admin)
Cloudflare Pages automatically deploys on every push to the connected branch. Nothing extra needed. Check in Pages dashboard → Deployments to see build history.
For Hetzner repos (api, kb) — GitHub Actions workflow
# Create file: .github/workflows/deploy.yml in your labelnex-api repo name: Deploy to Hetzner on: push: branches: [ main ] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Deploy to Hetzner via SSH uses: appleboy/ssh-action@v1.0.0 with: host: ${{ secrets.HETZNER_HOST }} username: labelnex key: ${{ secrets.HETZNER_SSH_KEY }} port: 2222 script: | cd /home/labelnex/apps/labelnex-api git pull origin main npm install --production pm2 restart labelnex-api pm2 save
Add GitHub repository secrets: Settings → Secrets → Actions:
HETZNER_HOST = your Hetzner IP · HETZNER_SSH_KEY = contents of your ~/.ssh/id_ed25519 private key
ufw allow from 0.0.0.0/0 to any port 2222 (already done in secure-hetzner.sh — port 2222 is open).Run this checklist after deploying each subdomain:
{"status":"ok","version":"..."}# SSH to Hetzner ssh labelnex-hetzner # Check all PM2 processes sudo -u labelnex pm2 status # View API logs (last 50 lines) sudo -u labelnex pm2 logs labelnex-api --lines 50 # Restart API after code update sudo -u labelnex pm2 restart labelnex-api # Check Nginx config nginx -t # Reload Nginx systemctl reload nginx # Check disk space df -h # Check latest backups ls -lh /home/labelnex/backups/ # Update code from GitHub (if repo is cloned on server) cd /home/labelnex/apps/labelnex-api && git pull origin main && npm install --production && pm2 restart labelnex-api # Check UFW firewall status ufw status verbose # Check fail2ban banned IPs fail2ban-client status sshd # Manually run daily backup bash /etc/cron.daily/labelnex-db-backup