<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: DeployHQ</title>
    <description>The latest articles on DEV Community by DeployHQ (@deployhq).</description>
    <link>https://dev.to/deployhq</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1687924%2F3c97db5e-145a-4aae-adbe-b57f149a6ec3.png</url>
      <title>DEV Community: DeployHQ</title>
      <link>https://dev.to/deployhq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/deployhq"/>
    <language>en</language>
    <item>
      <title>PR Radar vs GitHub Notifications vs Email: How Developers Actually Track Pull Requests</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Fri, 24 Apr 2026 12:32:48 +0000</pubDate>
      <link>https://dev.to/deployhq/pr-radar-vs-github-notifications-vs-email-how-developers-actually-track-pull-requests-4dp5</link>
      <guid>https://dev.to/deployhq/pr-radar-vs-github-notifications-vs-email-how-developers-actually-track-pull-requests-4dp5</guid>
      <description>&lt;p&gt;If you've managed more than a handful of pull requests across more than one Git platform, you've probably built your own little tracking system out of whatever was free and already installed. A few browser tabs. Email filters. Maybe a Slack integration that someone turned on two years ago and nobody can remember the auth for. It works, until it doesn't — until the PR that needed the &lt;q&gt;please merge today&lt;/q&gt; comment gets buried under twelve identical CI notifications, or until the GitLab side of your migration stays invisible for three hours because you only had the GitHub tab open.&lt;/p&gt;

&lt;p&gt;This is a comparison of the ways developers actually track PRs today — including &lt;a href="https://www.deployhq.com/features/pr-radar" rel="noopener noreferrer"&gt;PR Radar&lt;/a&gt;, the open-source Chrome extension we built because none of the existing options did what we wanted. We'll be honest about where each one wins and where it falls short, and we'll end with the one question that decides which tool you actually need. It's the same format we used for our &lt;a href="https://www.deployhq.com/blog/choosing-the-right-package-manager-npm-vs-yarn-vs-pnpm-vs-bun" rel="noopener noreferrer"&gt;package manager benchmark&lt;/a&gt; and our &lt;a href="https://dev.to/deployhq/mailtrap-vs-sendgrid-vs-mailgun-best-email-api-for-nodejs-in-2026-1m0-temp-slug-4999684"&gt;transactional email API comparison&lt;/a&gt; — practical, developer-to-developer, and honest about the gaps.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five options on the table
&lt;/h2&gt;

&lt;p&gt;Before the comparison, here's the shortlist. We've stayed focused on tools that &lt;strong&gt;monitor PR status&lt;/strong&gt; rather than tools that try to change your PR workflow (so Graphite, Aviator, and Reviewpad are out — they solve a different problem).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Email notifications&lt;/strong&gt; — the default for GitHub, GitLab, and Bitbucket&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub's built-in notification inbox&lt;/strong&gt; — the bell icon, the &lt;code&gt;/notifications&lt;/code&gt; page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slack integrations&lt;/strong&gt; — GitHub for Slack, GitLab for Slack, Bitbucket's bot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub-specific extensions&lt;/strong&gt; — Refined GitHub, Notifier for GitHub&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PR Radar&lt;/strong&gt; — multi-platform dashboard in the browser toolbar&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Option 1: Email — the default everyone ignores
&lt;/h2&gt;

&lt;p&gt;Every Git host turns on email notifications by default. Every developer turns most of them off within a month.&lt;/p&gt;

&lt;p&gt;The problem with email isn't that it's a bad channel. It's that PR events are high-frequency and low-information per message. A typical active PR generates email for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The initial open&lt;/li&gt;
&lt;li&gt;Every review comment&lt;/li&gt;
&lt;li&gt;Every reply in a thread&lt;/li&gt;
&lt;li&gt;Every push that re-triggers CI&lt;/li&gt;
&lt;li&gt;Every CI status change&lt;/li&gt;
&lt;li&gt;Merge or close&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Multiply by 8–12 active PRs in a normal week and you've built a firehose. The signal-to-noise ratio collapses because every email looks roughly the same in the preview pane: a PR title, a repo name, and one line of context. You cannot tell at a glance which of your fifteen unread messages is &lt;q&gt;CI failed on the PR that's blocking release&lt;/q&gt; and which is &lt;q&gt;approving nit on a docs typo.&lt;/q&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where email wins:&lt;/strong&gt; Audit trail. If you need a durable record of what happened and when, email is it. Good for compliance, useless for real-time awareness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it fails:&lt;/strong&gt; Real-time awareness, cross-platform visibility, CI status at a glance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Option 2: GitHub's notification inbox
&lt;/h2&gt;

&lt;p&gt;GitHub's inbox (the bell icon and &lt;code&gt;/notifications&lt;/code&gt;) is better than email for one reason: it's scoped and threaded. A single PR becomes one inbox entry that updates in place instead of twenty separate emails.&lt;/p&gt;

&lt;p&gt;It has genuine improvements over the last couple of years — filters, custom views, &lt;q&gt;Participating&lt;/q&gt; vs &lt;q&gt;All,&lt;/q&gt; per-repo mute. If you live entirely inside GitHub, the inbox is solid.&lt;/p&gt;

&lt;p&gt;But it has three hard ceilings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's GitHub-only.&lt;/strong&gt; If your team uses GitLab for infra and GitHub for apps, the inbox solves half your problem. The other half you track… somehow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It doesn't show CI status inline.&lt;/strong&gt; The inbox will tell you &lt;q&gt;CI ran on this PR.&lt;/q&gt; It will not tell you whether it passed or failed without a click into the PR page. For a tool whose primary job is telling you what needs your attention, burying the pass/fail state behind a click is a miss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It needs a tab open.&lt;/strong&gt; The inbox icon lives inside &lt;code&gt;github.com&lt;/code&gt;. If you closed the tab during a focus block, you find out about a failed deploy the next time you cmd-T your way back. Browser desktop notifications exist but are &lt;a href="https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications" rel="noopener noreferrer"&gt;disabled by default&lt;/a&gt; and, even when on, only fire when a GitHub tab is already open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it wins:&lt;/strong&gt; Native, reliable, no extra auth, granular filters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it fails:&lt;/strong&gt; GitHub-only, no CI status in the list view, tab-bound.&lt;/p&gt;




&lt;h2&gt;
  
  
  Option 3: Slack integrations
&lt;/h2&gt;

&lt;p&gt;Slack is where most engineering teams already are, so routing PR events into Slack seems obvious. And for a few specific use cases — a dedicated &lt;code&gt;#deploys&lt;/code&gt; channel, a release-critical PR that the whole team is watching — it's the right tool.&lt;/p&gt;

&lt;p&gt;For everyday PR tracking, Slack is a noise amplifier. A rebase-heavy afternoon on a single PR can push twenty notifications into a channel. The team stops reading them. Important signals (&lt;q&gt;production deploy failed&lt;/q&gt;) end up in the same channel as unimportant ones (&lt;q&gt;renovate bumped a patch version&lt;/q&gt;), with the same visual weight, both probably muted by Thursday.&lt;/p&gt;

&lt;p&gt;There's also a coverage asymmetry. GitHub for Slack is mature. GitLab for Slack is functional. Bitbucket's Slack integration exists but feels like an afterthought. If you've got repos on all three, Slack gives you an uneven view of each one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it wins:&lt;/strong&gt; Team visibility on critical events, shared context during an incident.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it fails:&lt;/strong&gt; Personal PR tracking, signal-to-noise, multi-platform parity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Option 4: Refined GitHub and Notifier for GitHub
&lt;/h2&gt;

&lt;p&gt;If you've looked for browser extensions for this before, you've hit two classic options.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/refined-github/refined-github" rel="noopener noreferrer"&gt;Refined GitHub&lt;/a&gt; is excellent — dozens of polish-level improvements to &lt;code&gt;github.com&lt;/code&gt; pages. But it enhances GitHub; it doesn't pull PR status &lt;em&gt;out&lt;/em&gt; of GitHub into your toolbar. You still need to open the tab.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notifier for GitHub&lt;/strong&gt; does put a badge in your toolbar, but the badge counts unread notifications — not CI state, not review state. A red badge tells you &lt;q&gt;there's something&lt;/q&gt; and nothing else. You click through to find out what.&lt;/p&gt;

&lt;p&gt;Both are GitHub-only, both assume GitHub is your whole world, and neither addresses the core visibility problem if your work spans platforms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where they win:&lt;/strong&gt; Free, lightweight, GitHub UX polish (Refined GitHub in particular is a staple).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where they fail:&lt;/strong&gt; Single-platform, no CI awareness in Notifier, no toolbar summary in Refined.&lt;/p&gt;




&lt;h2&gt;
  
  
  Option 5: PR Radar
&lt;/h2&gt;

&lt;p&gt;PR Radar is the extension we built after rotating through all four options above and still ending up with a browser window full of tabs. It's a Chrome extension (Firefox and Edge in progress), MIT-licensed, open source, and free — no paid tier, no accounts, no backend at all. Links to the store listing and repo are at the end of the post.&lt;/p&gt;

&lt;p&gt;The short version of what it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One toolbar popup&lt;/strong&gt; with every open PR across GitHub, GitLab, and Bitbucket&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Badge on the toolbar icon&lt;/strong&gt; with a live pass / fail / running count for CI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unresolved comment count&lt;/strong&gt; per PR, pulled via GraphQL so the number is actually correct&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment status inline&lt;/strong&gt; with clickable environment URLs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Desktop notifications and sound alerts&lt;/strong&gt; when CI finishes (no tab required — it polls in a service worker)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-click merge&lt;/strong&gt; from the dashboard, across all three platforms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stale PR dimming&lt;/strong&gt; with a configurable threshold so your attention goes to the active ones&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keyboard shortcuts&lt;/strong&gt; — &lt;code&gt;j&lt;/code&gt;/&lt;code&gt;k&lt;/code&gt; to move between PRs, &lt;code&gt;o&lt;/code&gt; to open, &lt;code&gt;/&lt;/code&gt; to search, &lt;code&gt;?&lt;/code&gt; for the full list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The design goal was minimum cognitive load: glance at the toolbar, see whether anything needs you, go back to what you were doing. If the badge is green, there is nothing to do. If it's red, you know exactly where to look. The same mindset drove our list of &lt;a href="https://dev.to/deployhq/6-developer-clis-that-ai-coding-agents-actually-use-well-5973-temp-slug-2135258"&gt;developer CLIs that AI agents use well&lt;/a&gt; — a good dev tool should compress ten decisions into one glance.&lt;/p&gt;

&lt;p&gt;Privacy-wise, PR Radar doesn't send your tokens anywhere. Your personal access tokens sit in the browser's local storage, and the extension makes API calls directly from your browser to each Git platform. There is no PR Radar backend. We don't run analytics. There's no &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; account required — the extension works whether or not you're a &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; customer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Side-by-side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Email&lt;/th&gt;
&lt;th&gt;GitHub Inbox&lt;/th&gt;
&lt;th&gt;Slack&lt;/th&gt;
&lt;th&gt;Refined / Notifier&lt;/th&gt;
&lt;th&gt;PR Radar&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitLab&lt;/td&gt;
&lt;td&gt;✅ (via email)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;⚠️ Uneven&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bitbucket&lt;/td&gt;
&lt;td&gt;✅ (via email)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;⚠️ Uneven&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI status at a glance&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;⚠️ In channel&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ Badge + inline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unresolved comment count&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deploy status in PR list&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works without a tab open&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ Service worker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sound / desktop alerts&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;⚠️ If tab open&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merge from the tool&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ All 3 platforms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Free tier + Slack cost&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Free + open source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privacy&lt;/td&gt;
&lt;td&gt;Platform stores email&lt;/td&gt;
&lt;td&gt;Platform&lt;/td&gt;
&lt;td&gt;Slack + platform&lt;/td&gt;
&lt;td&gt;Platform&lt;/td&gt;
&lt;td&gt;100% local, no backend&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern is consistent: each mainstream option handles one or two of these well and treats the rest as out of scope. For developers who only use GitHub and already live in Slack, that's fine. For anyone working across platforms, the gaps add up fast.&lt;/p&gt;




&lt;h2&gt;
  
  
  The deployment connection
&lt;/h2&gt;

&lt;p&gt;If you're reading this on the &lt;a href="https://www.deployhq.com/blog" rel="noopener noreferrer"&gt;DeployHQ blog&lt;/a&gt;, there's a fair chance you care specifically about the last mile: the moment between &lt;q&gt;CI passed&lt;/q&gt; and &lt;q&gt;change is live.&lt;/q&gt; That moment is where PR tracking stops being a productivity nice-to-have and starts being a deployment-quality thing.&lt;/p&gt;

&lt;p&gt;Two concrete examples:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Catching a broken deploy before the channel sees it.&lt;/strong&gt; When CI includes a deploy step (either an &lt;a href="https://www.deployhq.com/features/automatic-deployments" rel="noopener noreferrer"&gt;automatic deployment from Git&lt;/a&gt; or a &lt;a href="https://www.deployhq.com/features/build-pipelines" rel="noopener noreferrer"&gt;preview environment triggered by PR&lt;/a&gt;), the failure signal travels through the same notification pipes as every other CI event. In email or Slack, a failed deploy looks like every other failed check. In PR Radar, the toolbar flips red the moment the job changes state, with the deploy URL one click away. That's a four-second feedback loop instead of a forty-minute one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Merging without a tab full of PRs.&lt;/strong&gt; DeployHQ's &lt;a href="https://www.deployhq.com/features/one-click-rollback" rel="noopener noreferrer"&gt;one-click rollback&lt;/a&gt; and &lt;a href="https://www.deployhq.com/features/zero-downtime-deployments" rel="noopener noreferrer"&gt;zero-downtime deployments&lt;/a&gt; are designed to make shipping cheap. The bottleneck in that flow is usually human: somebody has to decide the PR is ready and hit merge. If that decision point lives in a popup rather than on a PR page buried in a tab group, the whole cycle tightens.&lt;/p&gt;

&lt;p&gt;Neither of these is the reason to install a browser extension on its own. But if you already care enough about deployment velocity to be reading this, the PR tracking side of the workflow probably deserves the same attention you've given &lt;a href="https://dev.to/deployhq/agentic-workflows-explained-how-ai-agents-are-changing-cicd-pipelines-nm0-temp-slug-2291085"&gt;your CI/CD pipeline&lt;/a&gt; and &lt;a href="https://www.deployhq.com/deploy-from-github" rel="noopener noreferrer"&gt;your deploy automation&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who should pick which
&lt;/h2&gt;

&lt;p&gt;Here's the actual decision tree.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You only use GitHub and you're fine with the inbox.&lt;/strong&gt; Stay with it, maybe bolt on Refined GitHub for UI polish. No extension needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You only use GitHub but the inbox feels lossy.&lt;/strong&gt; Try &lt;a href="https://chromewebstore.google.com/detail/notifier-for-github/lmjdlojahmbbcodnpecnjnmlddbkjhnn" rel="noopener noreferrer"&gt;Notifier for GitHub&lt;/a&gt; for the toolbar badge. If you want CI status specifically and not just unread counts, PR Radar fits even in single-platform setups.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You use GitHub + GitLab or GitHub + Bitbucket.&lt;/strong&gt; This is the case email, the inbox, and GitHub-specific extensions all fail at. A dedicated multi-platform tool is the only real answer. PR Radar is the one we built; there isn't much direct competition in the free / privacy-first slice of that category.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You're a team lead who wants team-wide visibility into deploys.&lt;/strong&gt; Slack is still right for that — route release-critical events into a dedicated channel. Just don't try to use the same Slack integration for personal PR tracking; it's the wrong tool for the wrong granularity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You care about privacy or you work on client repos with sensitive PR titles.&lt;/strong&gt; PR Radar keeps everything local — tokens in browser storage, API calls direct to platforms, no backend, no analytics. Hosted PR tools generally require you to authorize an OAuth app that sees your PR content. That's a real trade-off worth knowing about, in the same category as picking &lt;a href="https://dev.to/deployhq/6-must-have-mcp-servers-for-web-developers-in-2025-35no"&gt;which MCP servers you grant access to your codebase&lt;/a&gt; or &lt;a href="https://dev.to/deployhq/how-to-use-git-with-claude-code-understanding-the-co-authored-by-attribution-3boi"&gt;how you wire Claude Code into Git&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;PR Radar is free, open source (MIT), and takes about a minute to set up.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Install from the &lt;a href="https://chromewebstore.google.com/detail/pr-radar-pr-dashboard-ci/hkombgibegjffiadmekpiabdakkoidmh" rel="noopener noreferrer"&gt;Chrome Web Store&lt;/a&gt;&lt;/strong&gt; (Firefox and Edge builds are in progress on the &lt;a href="https://github.com/deployhq/pr-radar/releases" rel="noopener noreferrer"&gt;GitHub releases page&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Star the repo on &lt;a href="https://github.com/deployhq/pr-radar" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt; if it saves you a tab — it's how we know to keep investing in it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File an issue&lt;/strong&gt; if a platform quirk breaks your setup; we read all of them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're already on &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;, PR Radar closes the visibility gap between &lt;q&gt;PR looks good&lt;/q&gt; and &lt;q&gt;change is live&lt;/q&gt; without asking you to change anything about your deployment workflow. If you're not, it's still the fastest way we've found to stop tab-hopping between Git hosts.&lt;/p&gt;




&lt;p&gt;Questions, platform-specific gotchas, or feature requests? Email us at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or ping us on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt;. If you'd rather build your own PR dashboard on top of the GraphQL APIs, the source is MIT-licensed and PRs are welcome.&lt;/p&gt;

</description>
      <category>prs</category>
      <category>github</category>
      <category>gitlab</category>
      <category>bitbutcket</category>
    </item>
    <item>
      <title>Introducing the DeployHQ Chrome Extension: Deploy Without Leaving Your Browser</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Wed, 18 Mar 2026 07:43:07 +0000</pubDate>
      <link>https://dev.to/deployhq/introducing-the-deployhq-chrome-extension-deploy-without-leaving-your-browser-2abp</link>
      <guid>https://dev.to/deployhq/introducing-the-deployhq-chrome-extension-deploy-without-leaving-your-browser-2abp</guid>
      <description>&lt;p&gt;If you deploy web applications, you know the routine: switch to your deployment platform, find the right project, pick the branch, and hit deploy. It works, but it pulls you out of your flow every single time.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.deployhq.com/support/chrome-extension" rel="noopener noreferrer"&gt;DeployHQ Chrome Extension&lt;/a&gt; eliminates that context switch entirely. It brings deployment controls directly into GitHub, GitLab, and Bitbucket — the platforms where you already review and merge code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does the Chrome Extension Do?
&lt;/h2&gt;

&lt;p&gt;The extension adds a &lt;strong&gt;&lt;q&gt;Deploy with DeployHQ&lt;/q&gt;&lt;/strong&gt; button to your repository pages on GitHub, GitLab, and Bitbucket. Instead of navigating to a separate deployment dashboard, you trigger deployments from the same page where you just merged a pull request or pushed a commit.&lt;/p&gt;

&lt;p&gt;Here's what it covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One-click deploys&lt;/strong&gt; from repository root pages, branch pages, and pull/merge request pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project dashboard&lt;/strong&gt; with a searchable list of your &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; projects and their latest deployment timestamps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server and branch selection&lt;/strong&gt; so you can target specific environments before deploying&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time notifications&lt;/strong&gt; via desktop alerts when deployments start, succeed, or fail&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Badge indicators&lt;/strong&gt; on the extension icon — blue for active deployments, red for failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a repository isn't connected to &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; yet, the extension shows a &lt;strong&gt;&lt;q&gt;Connect to DeployHQ&lt;/q&gt;&lt;/strong&gt; link instead, making it easy to set up new projects without leaving your browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters for Your Workflow
&lt;/h2&gt;

&lt;p&gt;Most deployment friction isn't about the deploy itself — it's about the interruption. You finish a code review, approve the PR, merge it, and then... open a new tab, log in to your deployment tool, navigate to the right project, and trigger the deploy manually.&lt;/p&gt;

&lt;p&gt;With the Chrome Extension, the deploy button is right there on the pull request page. Merge and deploy in one smooth motion.&lt;/p&gt;

&lt;p&gt;This is especially useful if you practice &lt;a href="https://dev.to/deployhq/continuous-delivery-vs-continuous-deployment-whats-the-difference-ig-temp-slug-3043658"&gt;continuous delivery&lt;/a&gt; — where every merged change should reach staging or production quickly. The fewer steps between merge and deploy, the faster your feedback loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Visit the &lt;a href="https://chromewebstore.google.com/detail/deployhq/oigfcfnijljhhenakancnahkjhfhgdjn" rel="noopener noreferrer"&gt;Chrome Web Store listing&lt;/a&gt; and click &lt;strong&gt;Add to Chrome&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Confirm the installation when prompted&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The extension needs permissions for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt; — to save your credentials locally&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notifications&lt;/strong&gt; — to alert you about deployment status&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Site access&lt;/strong&gt; — to deployhq.com, github.com, gitlab.com, and bitbucket.org&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;Click the extension icon in your browser toolbar and enter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Your &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; subdomain&lt;/strong&gt; (e.g., &lt;code&gt;mycompany&lt;/code&gt; if your account is at &lt;code&gt;mycompany.deployhq.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your email address&lt;/strong&gt; associated with the account&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your API key&lt;/strong&gt; — find this in your &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; account under &lt;a href="https://www.deployhq.com/support/api" rel="noopener noreferrer"&gt;Settings &amp;gt; Security&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your credentials are stored locally in your browser and are never sent to any third party.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customisation
&lt;/h3&gt;

&lt;p&gt;The extension offers a few configurable options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polling interval&lt;/strong&gt; — how often it checks for deployment updates (default: 60 seconds)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notifications&lt;/strong&gt; — toggle desktop alerts on or off&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform integrations&lt;/strong&gt; — enable or disable the deploy button on GitHub, GitLab, or Bitbucket individually&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How It Fits with the Rest of DeployHQ
&lt;/h2&gt;

&lt;p&gt;The Chrome Extension is one of several ways to interact with &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; beyond the web dashboard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.deployhq.com/support/api" rel="noopener noreferrer"&gt;API&lt;/a&gt;&lt;/strong&gt; — full programmatic access for custom integrations and automation scripts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.deployhq.com/support/cli" rel="noopener noreferrer"&gt;CLI Tool&lt;/a&gt;&lt;/strong&gt; — trigger and manage deployments from your terminal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.deployhq.com/support/mcp-server" rel="noopener noreferrer"&gt;MCP Server&lt;/a&gt;&lt;/strong&gt; — integrate &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; with AI coding assistants like Claude Code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.deployhq.com/features/integrations" rel="noopener noreferrer"&gt;Integrations&lt;/a&gt;&lt;/strong&gt; — connect with Slack, Discord, Sentry, and other tools in your stack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you prefer a browser-based workflow, a terminal-first approach, or AI-assisted automation, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; meets you where you work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Source
&lt;/h2&gt;

&lt;p&gt;The Chrome Extension is fully open source. You can browse the code, report issues, or contribute improvements on the &lt;a href="https://github.com/deployhq/deployhq-chrome-extension" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you spot a bug or have a feature request, opening an issue there is the fastest way to get it addressed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;If you're already using &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;, install the extension and shave a few seconds off every deployment. Those seconds add up — especially on teams that deploy multiple times a day.&lt;/p&gt;

&lt;p&gt;If you haven't tried &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; yet, &lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;sign up for a free account&lt;/a&gt; and experience how simple deployment can be — from &lt;a href="https://www.deployhq.com/deploy-from-github" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://www.deployhq.com/deploy-from-gitlab" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;, or any Git repository.&lt;/p&gt;




&lt;p&gt;Have questions or feedback? Reach out to &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or find us on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>launches</category>
      <category>newfeatures</category>
      <category>chromeextension</category>
      <category>deployhq</category>
    </item>
    <item>
      <title>Continuous Delivery vs Continuous Deployment: What's the Difference?</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Fri, 13 Mar 2026 11:39:07 +0000</pubDate>
      <link>https://dev.to/deployhq/continuous-delivery-vs-continuous-deployment-whats-the-difference-3p2b</link>
      <guid>https://dev.to/deployhq/continuous-delivery-vs-continuous-deployment-whats-the-difference-3p2b</guid>
      <description>&lt;p&gt;Continuous delivery and continuous deployment sound almost identical, and most teams use the terms interchangeably. That confusion is costly. The difference between them determines who (or what) decides when your code reaches production, and getting that decision wrong can mean either unnecessary bottlenecks or uncontrolled releases hitting your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick definitions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Continuous delivery&lt;/strong&gt; means every code change is built, tested, and packaged into a release artifact that &lt;em&gt;could&lt;/em&gt; go to production at any moment. The pipeline does everything except the final step: a human decides when to press the deploy button. Your code is always in a deployable state, but deployment remains a deliberate business decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Continuous deployment&lt;/strong&gt; takes that one step further. Every change that passes the full automated test suite is deployed to production automatically, with no human gate. There is no deploy button. If the tests pass, your users see the change within minutes.&lt;/p&gt;

&lt;p&gt;The distinction comes down to a single question: &lt;strong&gt;is there a human approval step before production, or not?&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Code Commit] --&amp;gt; B[Build &amp;amp; Unit Tests]
    B --&amp;gt; C[Integration Tests]
    C --&amp;gt; D[Staging Deploy]
    D --&amp;gt; E{Human Approval?}
    E -- "Yes: Continuous Delivery" --&amp;gt; F[Manual Production Deploy]
    E -- "No: Continuous Deployment" --&amp;gt; G[Automatic Production Deploy]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  How they relate to continuous integration
&lt;/h2&gt;

&lt;p&gt;Neither continuous delivery nor continuous deployment works without continuous integration (CI) as the foundation. CI is the practice of merging code changes frequently and running automated builds and tests on every merge. It catches integration issues early and keeps the main branch in a working state.&lt;/p&gt;

&lt;p&gt;Think of it as a spectrum:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Continuous integration&lt;/strong&gt; --- automated build and test on every commit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous delivery&lt;/strong&gt; --- CI + automated release pipeline + manual deploy gate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous deployment&lt;/strong&gt; --- CI + automated release pipeline + automatic deploy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each level builds on the previous one. You cannot skip straight to continuous deployment without first having solid CI and a reliable delivery pipeline. For a deeper look at how these pieces fit together, see &lt;a href="https://www.deployhq.com/blog/what-is-ci-cd" rel="noopener noreferrer"&gt;What is CI/CD?&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    CI["Continuous Integration&amp;lt;br/&amp;gt;Build + Test on every commit"] --&amp;gt; CD_DELIVERY["Continuous Delivery&amp;lt;br/&amp;gt;+ Release pipeline&amp;lt;br/&amp;gt;+ Manual deploy gate"]
    CD_DELIVERY --&amp;gt; CD_DEPLOY["Continuous Deployment&amp;lt;br/&amp;gt;+ Automatic production deploy"]

    style CI fill:#e8f4f8,stroke:#2196F3
    style CD_DELIVERY fill:#fff3e0,stroke:#FF9800
    style CD_DEPLOY fill:#e8f5e9,stroke:#4CAF50

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key differences at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Continuous Delivery&lt;/th&gt;
&lt;th&gt;Continuous Deployment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment trigger&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual approval&lt;/td&gt;
&lt;td&gt;Automatic on passing tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Risk tolerance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lower --- human checkpoint catches edge cases&lt;/td&gt;
&lt;td&gt;Higher --- trusts the automated test suite completely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Release cadence&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;On-demand, when the business decides&lt;/td&gt;
&lt;td&gt;Every passing change, potentially dozens per day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Test requirements&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Very high --- tests are the &lt;em&gt;only&lt;/em&gt; gate to production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rollback strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pre-deployment review catches most issues&lt;/td&gt;
&lt;td&gt;Must have automated rollback and monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Regulated industries, complex coordinated releases&lt;/td&gt;
&lt;td&gt;SaaS products, web apps, fast-iteration teams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Team maturity needed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moderate --- need solid CI and release pipeline&lt;/td&gt;
&lt;td&gt;High --- need comprehensive tests, monitoring, and incident response&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The practical effect: with continuous delivery, a deploy to production is a one-click action that happens when someone decides the time is right. With continuous deployment, there is no &lt;q&gt;deploy action&lt;/q&gt; at all --- production updates are a side effect of merging code.&lt;/p&gt;




&lt;h2&gt;
  
  
  When continuous delivery is the right choice
&lt;/h2&gt;

&lt;p&gt;Continuous delivery is not the &lt;q&gt;lesser&lt;/q&gt; option. For many teams, it is the correct and deliberate choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regulated industries demand it.&lt;/strong&gt; If you are shipping software for healthcare, financial services, or government systems, compliance frameworks often require documented human approval before production changes. A payment processor cannot auto-deploy changes to transaction-handling code without a sign-off. Continuous delivery gives you the speed of an automated pipeline while preserving the audit trail regulators expect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coordinated releases need it.&lt;/strong&gt; When a release involves database migrations, API version bumps, third-party integrations, or marketing launches, you need to control timing. Continuous delivery lets the pipeline prepare everything, then a team lead triggers the deploy when all the pieces are aligned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Incomplete test coverage warrants it.&lt;/strong&gt; If your automated test suite does not cover critical edge cases --- and honestly, most test suites have gaps --- a human review step before production is a reasonable safety net. The goal is to close those gaps over time, but in the meantime, delivery gives you a responsible middle ground.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business timing matters.&lt;/strong&gt; Sometimes you do not want changes going live on a Friday afternoon, during a peak traffic window, or before a coordinated announcement. Continuous delivery decouples &lt;q&gt;ready to ship&lt;/q&gt; from &lt;q&gt;shipped.&lt;/q&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: fintech startup
&lt;/h3&gt;

&lt;p&gt;A payments team has 40 microservices. Their core transaction services use continuous delivery: every merge to main triggers the full pipeline and deploys to staging automatically, but production deploys require a senior engineer to approve in the deployment dashboard. Their internal tooling services, which are lower risk, use continuous deployment. This mixed approach balances speed with the compliance requirements their banking partners enforce.&lt;/p&gt;




&lt;h2&gt;
  
  
  When continuous deployment makes sense
&lt;/h2&gt;

&lt;p&gt;Continuous deployment is not reckless --- it is a sign that your engineering practices have matured to the point where you trust your automated systems more than manual review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SaaS products thrive on it.&lt;/strong&gt; When your product is a web application with thousands of users, shipping small changes frequently reduces the blast radius of any single deploy. A five-line CSS fix and a critical security patch go through the same pipeline. Both are live within minutes of merging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comprehensive test suites enable it.&lt;/strong&gt; If your team has invested in unit tests, integration tests, end-to-end tests, contract tests, and performance benchmarks that collectively cover the critical paths, those tests are a more reliable gate than a tired engineer reviewing a pull request at 4pm. Continuous deployment works when you trust the tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature flags provide a safety net.&lt;/strong&gt; Continuous deployment does not mean every user sees every change immediately. Teams using &lt;a href="https://www.deployhq.com/blog/what-are-feature-flags" rel="noopener noreferrer"&gt;feature flags&lt;/a&gt; can deploy code to production without activating it, then gradually roll out to a percentage of users. If something breaks, disable the flag --- no rollback needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Microservices architectures benefit.&lt;/strong&gt; When services are independently deployable, continuous deployment per service is natural. Each team owns their deploy pipeline and cadence. A change to the notification service does not need to coordinate with the billing service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: SaaS startup
&lt;/h3&gt;

&lt;p&gt;A 15-person engineering team runs a project management SaaS. They merge to main 20-30 times per day. Every merge triggers the pipeline: build, 2,000+ automated tests, deploy to canary, monitor error rates for 5 minutes, then promote to full production. Average time from merge to live: 12 minutes. They find and fix issues faster than teams that batch releases weekly because each deploy is small and easy to reason about.&lt;/p&gt;




&lt;h2&gt;
  
  
  The hybrid approach most teams actually use
&lt;/h2&gt;

&lt;p&gt;In practice, very few organisations pick one approach exclusively. The most effective teams use different strategies for different contexts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Continuous deployment to staging, continuous delivery to production.&lt;/strong&gt; Every merge auto-deploys to a staging environment where QA, product managers, and stakeholders can review. Production deploys happen when the team is ready. This is the most common pattern for teams transitioning from manual deployments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Different pipelines for different risk levels.&lt;/strong&gt; Frontend changes to marketing pages might auto-deploy. Backend changes to authentication or billing go through a manual approval gate. The risk profile of the change determines the process, not a blanket policy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Feature flags bridge the gap.&lt;/strong&gt; With feature flags, you can have continuous deployment (code goes to production automatically) while maintaining continuous delivery semantics (the feature is not &lt;q&gt;released&lt;/q&gt; until someone enables the flag). This gives you the deployment speed of CD with the release control of delivery.&lt;/p&gt;

&lt;p&gt;Understanding &lt;a href="https://dev.to/deployhq/what-is-software-deployment-a-complete-guide-23n2-temp-slug-2309058"&gt;what software deployment actually involves&lt;/a&gt; helps you design these pipelines with the right level of automation for each stage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common misconceptions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;q&gt;Continuous deployment means no testing.&lt;/q&gt;&lt;/strong&gt; This is backwards. Continuous deployment requires &lt;em&gt;more&lt;/em&gt; testing than continuous delivery, not less. When tests are the only gate to production, they must be comprehensive, fast, and reliable. Teams doing continuous deployment typically invest heavily in test infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;q&gt;Continuous delivery is just manual deployment.&lt;/q&gt;&lt;/strong&gt; No. With continuous delivery, the entire pipeline is automated --- build, test, package, deploy to staging, run acceptance tests. The &lt;em&gt;only&lt;/em&gt; manual step is the final approval to promote to production. That approval might be a single click in a dashboard, not a manual deployment process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;q&gt;You need continuous deployment to move fast.&lt;/q&gt;&lt;/strong&gt; Not necessarily. A team doing continuous delivery with a one-click deploy can ship a hotfix to production in under 5 minutes. The difference between &lt;q&gt;auto-deploy on merge&lt;/q&gt; and &lt;q&gt;click a button after merge&lt;/q&gt; is seconds, not hours. The real speed gains come from CI and automated pipelines, which both approaches share.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;q&gt;Continuous deployment is always the goal.&lt;/q&gt;&lt;/strong&gt; It depends entirely on your context. A hospital's medical device software should not auto-deploy to production. A personal blog's deployment pipeline probably should. The &lt;q&gt;best&lt;/q&gt; approach is the one that matches your risk tolerance, regulatory requirements, and team capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;q&gt;You have to choose one.&lt;/q&gt;&lt;/strong&gt; As covered in the hybrid section, most teams use both. The question is not &lt;q&gt;which one?&lt;/q&gt; but &lt;q&gt;which one for this particular service or change?&lt;/q&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How \DeployHQ supports both approaches
&lt;/h2&gt;

&lt;p&gt;Whether your team practises continuous delivery, continuous deployment, or a hybrid of both, DeployHQ handles the deployment step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For continuous delivery workflows&lt;/strong&gt; , DeployHQ deploys automatically when you push to a branch, but you control which branch triggers a deploy and when you merge to it. Your deployment dashboard shows pending deployments, lets you review what will change, and gives you a one-click deploy when you are ready. You stay in control of timing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For continuous deployment workflows&lt;/strong&gt; , configure DeployHQ to deploy on every push to your main branch. Pair this with your CI server --- when your tests pass and code merges, DeployHQ picks up the change and deploys it automatically. Your CI pipeline is the gate; DeployHQ is the delivery mechanism.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.deployhq.com/features/zero-downtime-deployments" rel="noopener noreferrer"&gt;Zero-downtime deployments&lt;/a&gt;&lt;/strong&gt; are essential for continuous deployment. DeployHQ supports atomic deployments that swap the live version only after the new version is fully uploaded, so your users never see a half-deployed state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.deployhq.com/features/build-pipelines" rel="noopener noreferrer"&gt;Build pipelines&lt;/a&gt;&lt;/strong&gt; let you run build commands (npm install, webpack, composer install) on \DeployHQ's servers before deploying. This means your CI server handles testing while DeployHQ handles building and deploying --- a clean separation of concerns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One-click rollback&lt;/strong&gt; provides a safety net for both approaches. If a deploy introduces a problem, roll back to the previous version in seconds from the DeployHQ dashboard.&lt;/p&gt;

&lt;p&gt;For teams &lt;a href="https://www.deployhq.com/deploy-from-github" rel="noopener noreferrer"&gt;deploying from GitHub&lt;/a&gt;, setting up &lt;a href="https://www.deployhq.com/features/automatic-deployments" rel="noopener noreferrer"&gt;automatic deployments&lt;/a&gt; takes minutes. Push to main, DeployHQ deploys. Push to staging, DeployHQ deploys to your staging server. Different branches, different servers, different strategies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting started: a practical path
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;If you are not doing either yet&lt;/strong&gt; , start with continuous integration. Get automated builds and tests running on every commit. Once your team trusts the CI pipeline, add automated staging deploys --- that is continuous delivery. For a step-by-step guide, see &lt;a href="https://dev.to/deployhq/cicd-pipelines-the-complete-guide-9p5-temp-slug-1919683"&gt;CI/CD Pipelines: The Complete Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you are doing continuous delivery&lt;/strong&gt; , evaluate whether your test suite is comprehensive enough to remove the manual gate. Track how often the human approval step actually catches issues that tests missed. If the answer is &lt;q&gt;rarely,&lt;/q&gt; you might be ready for continuous deployment --- at least for lower-risk services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you are doing continuous deployment&lt;/strong&gt; , make sure your safety nets are solid. Automated rollback should be fast (under 60 seconds). Monitoring should alert on error rate spikes within minutes. Incident response should be practised, not theoretical. And review whether &lt;a href="https://www.deployhq.com/blog/what-is-continuous-deployment" rel="noopener noreferrer"&gt;continuous deployment as a practice&lt;/a&gt; is working for every service, or if some should move back to delivery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The maturity path looks like this:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A["Manual Deployments&amp;lt;br/&amp;gt;FTP uploads, SSH scripts"] --&amp;gt; B["Continuous Integration&amp;lt;br/&amp;gt;Automated build + test"]
    B --&amp;gt; C["Continuous Delivery&amp;lt;br/&amp;gt;Automated pipeline + manual gate"]
    C --&amp;gt; D["Continuous Deployment&amp;lt;br/&amp;gt;Fully automated to production"]
    C --&amp;gt; E["Hybrid Approach&amp;lt;br/&amp;gt;CD for low-risk, Delivery for high-risk"]
    D --&amp;gt; E

    style A fill:#ffebee,stroke:#f44336
    style B fill:#e8f4f8,stroke:#2196F3
    style C fill:#fff3e0,stroke:#FF9800
    style D fill:#e8f5e9,stroke:#4CAF50
    style E fill:#f3e5f5,stroke:#9C27B0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most teams land on the hybrid approach, and that is perfectly fine. The goal is not to reach the &lt;q&gt;end&lt;/q&gt; of the spectrum --- it is to find the deployment strategy that lets your team ship confidently and frequently.&lt;/p&gt;




&lt;p&gt;Ready to automate your deployments? &lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;Sign up for \DeployHQ&lt;/a&gt; and set up your first deployment pipeline in minutes, whether you are practising continuous delivery, continuous deployment, or both.&lt;/p&gt;

&lt;p&gt;If you have questions about setting up your deployment workflow, reach out at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or find us on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;Twitter/X @deployhq&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devopsinfrastructure</category>
      <category>whatis</category>
      <category>continuousdelivery</category>
      <category>continuousdeployment</category>
    </item>
    <item>
      <title>CI/CD Pipelines: The Complete Guide</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Fri, 13 Mar 2026 11:39:02 +0000</pubDate>
      <link>https://dev.to/deployhq/cicd-pipelines-the-complete-guide-1n3k</link>
      <guid>https://dev.to/deployhq/cicd-pipelines-the-complete-guide-1n3k</guid>
      <description>&lt;p&gt;If you already know &lt;a href="https://www.deployhq.com/blog/what-is-ci-cd" rel="noopener noreferrer"&gt;what CI/CD is&lt;/a&gt;, the next question is practical: how do you actually build a pipeline that works for your team? This guide walks through designing, configuring, securing, and optimizing a CI/CD pipeline from scratch. No enterprise-scale assumptions — just actionable steps you can implement today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Anatomy of a CI/CD Pipeline
&lt;/h2&gt;

&lt;p&gt;Every CI/CD pipeline follows the same fundamental flow, regardless of the tools you use. Code moves through a series of automated stages, each acting as a quality gate before the next.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Source] --&amp;gt; B[Build]
    B --&amp;gt; C[Test]
    C --&amp;gt; D[Deploy]
    D --&amp;gt; E[Monitor]
    E --&amp;gt;|Rollback| D

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt; is where everything starts. A commit or pull request triggers the pipeline. Your CI system detects the change, checks out the code, and begins processing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build&lt;/strong&gt; compiles your application, installs dependencies, and produces deployable artifacts. For interpreted languages like Python or PHP, this might just be dependency installation and asset compilation. For compiled languages like Go or Java, it includes the actual compilation step. For a deeper look at &lt;a href="https://www.deployhq.com/blog/what-is-a-build-pipeline" rel="noopener noreferrer"&gt;what a build pipeline is&lt;/a&gt; and how to structure one, start there. The &lt;a href="https://www.deployhq.com/features/build-pipelines" rel="noopener noreferrer"&gt;build pipelines feature&lt;/a&gt; in &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; lets you run build commands as part of the deployment process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test&lt;/strong&gt; runs your automated test suite against the built artifacts. This is the most critical gate — if tests fail, the pipeline stops and nothing reaches production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy&lt;/strong&gt; pushes the tested artifacts to your target environment. This could be a staging server for manual review or production for fully automated continuous deployment. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles this stage with support for &lt;a href="https://www.deployhq.com/features/automatic-deployments" rel="noopener noreferrer"&gt;automatic deployments&lt;/a&gt; triggered by webhooks or API calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitor&lt;/strong&gt; watches the deployed application for errors, performance degradation, or unexpected behavior. Good monitoring closes the feedback loop — if something breaks, you catch it before your users do.&lt;/p&gt;




&lt;h2&gt;
  
  
  Designing Your Pipeline
&lt;/h2&gt;

&lt;p&gt;Before writing any configuration, make three decisions that shape everything else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Branch Strategy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Trunk-based development&lt;/strong&gt; works best for small-to-medium teams. Everyone commits to &lt;code&gt;main&lt;/code&gt; (or short-lived feature branches that merge within a day or two). The pipeline runs on every push to &lt;code&gt;main&lt;/code&gt;, and deployments happen frequently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitFlow&lt;/strong&gt; uses long-lived &lt;code&gt;develop&lt;/code&gt;, &lt;code&gt;release&lt;/code&gt;, and &lt;code&gt;hotfix&lt;/code&gt; branches. It adds ceremony but gives you more control over what reaches production and when. If your release cycle is weekly or longer, GitFlow might make sense.&lt;/p&gt;

&lt;p&gt;For most teams, trunk-based development with feature flags is the simpler, faster option. You deploy more often, which means smaller changes, which means fewer things break.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment Strategy
&lt;/h3&gt;

&lt;p&gt;A typical setup uses three environments:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Environment&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Deploys from&lt;/th&gt;
&lt;th&gt;Audience&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Development&lt;/td&gt;
&lt;td&gt;Integration testing&lt;/td&gt;
&lt;td&gt;Feature branches&lt;/td&gt;
&lt;td&gt;Developers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Staging&lt;/td&gt;
&lt;td&gt;Pre-production validation&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;main&lt;/code&gt; branch&lt;/td&gt;
&lt;td&gt;QA, stakeholders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production&lt;/td&gt;
&lt;td&gt;Live application&lt;/td&gt;
&lt;td&gt;Tagged releases or &lt;code&gt;main&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Users&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can start with just staging and production. Add environments only when you have a concrete reason — each one adds maintenance overhead and slows your feedback loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delivery vs. Deployment
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Continuous delivery&lt;/strong&gt; means every commit that passes the pipeline is &lt;em&gt;ready&lt;/em&gt; to deploy, but a human clicks the button. &lt;strong&gt;Continuous deployment&lt;/strong&gt; means every passing commit goes to production automatically, with no manual gate. The &lt;a href="https://dev.to/alex_morgan_2e6f2f637b128/continuous-delivery-vs-continuous-deployment-whats-the-difference-c6f-temp-slug-5636075"&gt;comparison between these approaches&lt;/a&gt; is worth reading if you are deciding which to adopt.&lt;/p&gt;

&lt;p&gt;Start with continuous delivery. Move to continuous deployment once your test suite is comprehensive enough that you trust it to catch regressions without human review.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pipeline Configuration Example
&lt;/h2&gt;

&lt;p&gt;Here is a real GitHub Actions workflow that builds a Node.js application, runs tests in parallel, and triggers a &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; deployment on success.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI/CD Pipeline&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;NODE_VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.NODE_VERSION }}&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-output&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dist/&lt;/span&gt;
          &lt;span class="na"&gt;retention-days&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

  &lt;span class="na"&gt;test-unit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.NODE_VERSION }}&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run test:unit -- --shard=${{ matrix.shard }}&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;shard&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;1/3&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;2/3&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3/3&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="na"&gt;test-integration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
          &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.NODE_VERSION }}&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run test:integration&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://postgres:test@localhost:5432/test&lt;/span&gt;

  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;test-unit&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;test-integration&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.ref == 'refs/heads/main' &amp;amp;&amp;amp; github.event_name == 'push'&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Trigger DeployHQ deployment&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;curl -s -X POST \&lt;/span&gt;
            &lt;span class="s"&gt;"${{ secrets.DEPLOYHQ_WEBHOOK_URL }}" \&lt;/span&gt;
            &lt;span class="s"&gt;-H "Content-Type: application/json" \&lt;/span&gt;
            &lt;span class="s"&gt;-d '{"branch": "main"}'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things to notice in this configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dependency caching&lt;/strong&gt; (&lt;code&gt;cache: 'npm'&lt;/code&gt;) avoids re-downloading packages on every run. This alone can cut 30-60 seconds off each build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test sharding&lt;/strong&gt; splits unit tests across three parallel runners. A test suite that takes 6 minutes sequentially finishes in roughly 2 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeployHQ as the CD step&lt;/strong&gt; keeps your deployment logic out of CI. The webhook triggers DeployHQ, which handles the actual file transfer, build commands, and server configuration. This separation means you can change your CI provider without touching your deployment setup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are deploying &lt;a href="https://www.deployhq.com/deploy-from-github" rel="noopener noreferrer"&gt;from GitHub&lt;/a&gt;, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; can also detect pushes directly without the webhook — but using a webhook gives you the option to deploy only after CI passes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing in Your Pipeline
&lt;/h2&gt;

&lt;p&gt;Not all tests belong in every pipeline run. The testing pyramid helps you decide what to run and when.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
    subgraph Pyramid["Testing Pyramid"]
        direction TB
        E2E["E2E Tests\n(few, slow, high confidence)"]
        INT["Integration Tests\n(moderate count, moderate speed)"]
        UNIT["Unit Tests\n(many, fast, focused)"]
    end
    E2E --- INT
    INT --- UNIT

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Unit tests&lt;/strong&gt; run on every commit. They are fast (seconds to low minutes), test individual functions in isolation, and catch logic errors early. If your unit tests take longer than 3 minutes, split them into parallel shards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integration tests&lt;/strong&gt; run on every push to &lt;code&gt;main&lt;/code&gt; and on pull requests. They verify that components work together — database queries return expected results, API endpoints respond correctly, services communicate as expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;End-to-end tests&lt;/strong&gt; run before production deployments, ideally on a staging environment. They simulate real user workflows through a browser. E2E tests are slow and brittle, so keep the count low — cover critical paths (signup, checkout, core features) and nothing more.&lt;/p&gt;

&lt;p&gt;The key principle: &lt;strong&gt;fast feedback first&lt;/strong&gt;. A developer should know within 2-3 minutes whether their change broke something. Push expensive tests later in the pipeline where they do not block the inner development loop.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment Strategies
&lt;/h2&gt;

&lt;p&gt;How code reaches production matters as much as how it is tested. The right strategy depends on your risk tolerance, infrastructure, and team size. For a broader look at &lt;a href="https://dev.to/deployhq/what-is-software-deployment-a-complete-guide-23n2-temp-slug-2309058"&gt;what software deployment involves&lt;/a&gt;, start there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct Deployment
&lt;/h3&gt;

&lt;p&gt;The simplest approach: upload new files and replace the old ones. This works for static sites and small applications where a few seconds of downtime during the swap is acceptable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zero-Downtime Deployment
&lt;/h3&gt;

&lt;p&gt;Uses symlink switching to swap between releases atomically. The new version is uploaded to a fresh directory, and once ready, the web server's document root symlink is flipped to point at it. There is no moment where the application is unavailable. DeployHQ supports this natively — see &lt;a href="https://www.deployhq.com/features/zero-downtime-deployments" rel="noopener noreferrer"&gt;zero-downtime deployments with DeployHQ&lt;/a&gt; for the setup walkthrough.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blue-Green Deployment
&lt;/h3&gt;

&lt;p&gt;Maintains two identical production environments. Traffic routes to &lt;q&gt;blue&lt;/q&gt; while &lt;q&gt;green&lt;/q&gt; receives the new deployment. After validation, traffic switches from blue to green.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    LB[Load Balancer]
    LB --&amp;gt;|Active| Blue[Blue Environment\nv1.2.3]
    LB -.-&amp;gt;|Standby| Green[Green Environment\nv1.2.4]
    Green --&amp;gt;|Validated| Switch{Switch Traffic}
    Switch --&amp;gt;|Cutover| LB

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The advantage is instant rollback — just switch traffic back to blue. The downside is cost: you are running two full environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Canary Deployment
&lt;/h3&gt;

&lt;p&gt;Routes a small percentage of traffic (typically 5-10%) to the new version while the rest continues hitting the current version. If error rates stay flat, you gradually increase the percentage until 100% of traffic reaches the new version.&lt;/p&gt;

&lt;p&gt;Canary deployments catch issues that only surface under real traffic patterns — race conditions, performance regressions at scale, edge cases your test suite missed. They require a load balancer that supports weighted routing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rolling Deployment
&lt;/h3&gt;

&lt;p&gt;Updates servers one at a time (or in small batches) behind a load balancer. At any point during the rollout, some servers run the old version and some run the new. This works well for stateless applications but can cause issues if the old and new versions are not compatible with each other (different database schemas, changed API contracts).&lt;/p&gt;

&lt;p&gt;For most small-to-medium teams, &lt;strong&gt;zero-downtime deployment via symlink switching&lt;/strong&gt; hits the sweet spot — no downtime, simple rollback, and no extra infrastructure cost.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security in CI/CD Pipelines
&lt;/h2&gt;

&lt;p&gt;Your pipeline has access to production credentials, source code, and deployment infrastructure. Treat it as a high-value attack surface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secrets Management
&lt;/h3&gt;

&lt;p&gt;Never hardcode secrets in pipeline configuration files or repository code. Use your CI provider's encrypted secrets store (GitHub Actions Secrets, GitLab CI Variables, etc.) and inject them as environment variables at runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Good: secrets injected at runtime&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DATABASE_URL }}&lt;/span&gt;
  &lt;span class="na"&gt;API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.API_KEY }}&lt;/span&gt;

&lt;span class="c1"&gt;# Bad: secrets in the repository (never do this)&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres://admin:password123@db.example.com/prod"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rotate secrets on a schedule. If a secret is ever exposed in logs or a commit, rotate it immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency Scanning
&lt;/h3&gt;

&lt;p&gt;Automated tools like &lt;code&gt;npm audit&lt;/code&gt;, Dependabot, or Snyk scan your dependency tree for known vulnerabilities. Run these on every pull request — they add seconds to the pipeline and catch issues before they merge.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Audit dependencies&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm audit --audit-level=high&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Static Analysis (SAST)
&lt;/h3&gt;

&lt;p&gt;Static Application Security Testing scans your source code for common vulnerabilities: SQL injection, XSS, insecure deserialization, hardcoded credentials. Tools like Semgrep, SonarQube, or CodeQL integrate directly into CI workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Supply Chain Security
&lt;/h3&gt;

&lt;p&gt;Lock files (&lt;code&gt;package-lock.json&lt;/code&gt;, &lt;code&gt;Gemfile.lock&lt;/code&gt;, &lt;code&gt;poetry.lock&lt;/code&gt;) pin exact dependency versions. Always commit them and use deterministic install commands (&lt;code&gt;npm ci&lt;/code&gt; instead of &lt;code&gt;npm install&lt;/code&gt;) to prevent supply chain attacks where a compromised package publishes a malicious minor version.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment Permissions
&lt;/h3&gt;

&lt;p&gt;Use role-based access control (RBAC) for deployment. Not everyone who can merge code should be able to deploy to production. DeployHQ supports &lt;a href="https://www.deployhq.com/pricing" rel="noopener noreferrer"&gt;team permissions&lt;/a&gt; that let you restrict who can trigger deployments to specific environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Monitoring and Rollback
&lt;/h2&gt;

&lt;p&gt;Deploying is only half the job. You need to know whether the deployment actually worked.&lt;/p&gt;

&lt;h3&gt;
  
  
  Post-Deployment Health Checks
&lt;/h3&gt;

&lt;p&gt;After every deployment, run automated checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTTP health endpoint&lt;/strong&gt; returns 200 with expected response body&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database connectivity&lt;/strong&gt; confirmed via a lightweight query&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;External service dependencies&lt;/strong&gt; are reachable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key business metrics&lt;/strong&gt; (error rate, response time) remain within normal bounds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any check fails within the first 5 minutes, trigger a rollback automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automated Rollback
&lt;/h3&gt;

&lt;p&gt;Define clear rollback triggers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Threshold&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HTTP 5xx error rate&lt;/td&gt;
&lt;td&gt;&amp;gt;5% for 2 minutes&lt;/td&gt;
&lt;td&gt;Auto-rollback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response time p95&lt;/td&gt;
&lt;td&gt;&amp;gt;2x baseline for 5 minutes&lt;/td&gt;
&lt;td&gt;Alert + manual rollback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health check failure&lt;/td&gt;
&lt;td&gt;Any check fails&lt;/td&gt;
&lt;td&gt;Auto-rollback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Critical exception spike&lt;/td&gt;
&lt;td&gt;&amp;gt;10x normal rate&lt;/td&gt;
&lt;td&gt;Auto-rollback&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;DeployHQ provides one-click rollback to any previous deployment, which makes recovery a matter of seconds rather than minutes. For a deeper look at how &lt;a href="https://www.deployhq.com/blog/deployment-automation-a-quick-overview" rel="noopener noreferrer"&gt;deployment automation&lt;/a&gt; reduces recovery time, that guide covers the specifics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error Monitoring
&lt;/h3&gt;

&lt;p&gt;Integrate error tracking (Sentry, Bugsnag, Honeybadger) into your application. These tools catch unhandled exceptions, group them by root cause, and alert your team. The deployment marker feature in most error trackers lets you correlate error spikes with specific deployments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pipeline Performance Optimization
&lt;/h2&gt;

&lt;p&gt;A slow pipeline kills developer productivity. If your pipeline takes 20 minutes, developers context-switch while waiting, and feedback arrives too late to be useful. Target under 10 minutes for the full pipeline — under 5 minutes is excellent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache Dependencies
&lt;/h3&gt;

&lt;p&gt;Every major CI platform supports dependency caching. Use it.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Cache target&lt;/th&gt;
&lt;th&gt;Typical savings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Node.js&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;~/.npm&lt;/code&gt; or &lt;code&gt;node_modules&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;30-90 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.cache/pip&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;20-60 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ruby&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vendor/bundle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;30-90 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/go/pkg/mod&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;15-45 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Parallelize Test Suites
&lt;/h3&gt;

&lt;p&gt;Split tests across multiple runners. GitHub Actions supports matrix strategies that shard your test suite automatically. Three shards typically give you a 2.5-3x speedup for minimal additional cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Incremental Builds
&lt;/h3&gt;

&lt;p&gt;Only rebuild what changed. Monorepo tools like Nx and Turborepo track file dependencies and skip unchanged packages. For simpler projects, check whether source files changed before running expensive steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check if build needed&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;changes&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;if git diff --name-only HEAD~1 | grep -q '^src/'; then&lt;/span&gt;
      &lt;span class="s"&gt;echo "build_needed=true" &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;
    &lt;span class="s"&gt;fi&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.changes.outputs.build_needed == 'true'&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Artifact Reuse
&lt;/h3&gt;

&lt;p&gt;Build once, deploy the same artifact everywhere. Upload build artifacts in the build job and download them in subsequent jobs instead of rebuilding. This ensures what you tested is exactly what you deploy — no &lt;q&gt;it worked on CI&lt;/q&gt; surprises.&lt;/p&gt;




&lt;h2&gt;
  
  
  Measuring Pipeline Effectiveness
&lt;/h2&gt;

&lt;p&gt;The DORA (DevOps Research and Assessment) metrics are the industry standard for measuring how well your delivery process performs. Track these four metrics to know whether your pipeline is actually helping.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Elite&lt;/th&gt;
&lt;th&gt;High&lt;/th&gt;
&lt;th&gt;Medium&lt;/th&gt;
&lt;th&gt;Low&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment frequency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;On-demand (multiple/day)&lt;/td&gt;
&lt;td&gt;Weekly to monthly&lt;/td&gt;
&lt;td&gt;Monthly to 6-monthly&lt;/td&gt;
&lt;td&gt;Fewer than once per 6 months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lead time for changes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Less than 1 hour&lt;/td&gt;
&lt;td&gt;1 day to 1 week&lt;/td&gt;
&lt;td&gt;1 week to 1 month&lt;/td&gt;
&lt;td&gt;More than 1 month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mean time to recovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Less than 1 hour&lt;/td&gt;
&lt;td&gt;Less than 1 day&lt;/td&gt;
&lt;td&gt;1 day to 1 week&lt;/td&gt;
&lt;td&gt;More than 1 week&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Change failure rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0-15%&lt;/td&gt;
&lt;td&gt;16-30%&lt;/td&gt;
&lt;td&gt;31-45%&lt;/td&gt;
&lt;td&gt;46-60%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Source: DORA State of DevOps Report&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You do not need to be &lt;q&gt;elite&lt;/q&gt; across all four metrics. Focus on the ones that hurt most. If your change failure rate is high, invest in testing. If your lead time is long, look for pipeline bottlenecks and manual approval gates that could be automated.&lt;/p&gt;

&lt;p&gt;The relationship between these metrics matters too. Deploying more frequently &lt;em&gt;reduces&lt;/em&gt; change failure rate because each deployment is smaller and easier to reason about. Faster mean time to recovery comes from having reliable rollback and good monitoring — not from deploying less often.&lt;/p&gt;




&lt;h2&gt;
  
  
  CI/CD for Small Teams
&lt;/h2&gt;

&lt;p&gt;Most CI/CD content assumes you have a dedicated platform team, a Kubernetes cluster, and a budget for enterprise tools. If that is not you, here is a pragmatic approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start With Two Tools
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;GitHub Actions for CI&lt;/strong&gt; handles building, testing, and code quality checks. The free tier includes 2,000 minutes per month for private repositories — enough for most small teams. GitLab CI/CD and Bitbucket Pipelines offer similar free tiers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;\DeployHQ for CD&lt;/strong&gt; handles getting your code onto servers. It connects to your GitHub repository, runs build commands on deployment, and transfers files to your servers over SSH, SFTP, or to cloud storage. The separation between CI and CD tools means you can swap either one independently.&lt;/p&gt;

&lt;p&gt;For a comparison of deployment tools available, the &lt;a href="https://dev.to/deployhq/best-software-deployment-tools-in-2026-3aaa-temp-slug-4453201"&gt;best software deployment tools guide&lt;/a&gt; covers the landscape.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Do Not Need (Yet)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins&lt;/strong&gt; : Powerful but requires its own server, maintenance, and plugin management. Overkill for teams under 10.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ArgoCD/Flux&lt;/strong&gt; : GitOps controllers for Kubernetes. If you are not running Kubernetes, skip these entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom deployment scripts&lt;/strong&gt; : They work until they do not. A managed tool like DeployHQ eliminates an entire class of &lt;q&gt;it works on my machine&lt;/q&gt; deployment bugs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microservices&lt;/strong&gt; : A monolith with a clean CI/CD pipeline ships faster than microservices with manual deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cost Considerations
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Free tier&lt;/th&gt;
&lt;th&gt;Paid from&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Actions&lt;/td&gt;
&lt;td&gt;2,000 min/month (private)&lt;/td&gt;
&lt;td&gt;$0.008/min overage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitLab CI/CD&lt;/td&gt;
&lt;td&gt;400 min/month&lt;/td&gt;
&lt;td&gt;$10/month for 10,000 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;\DeployHQ&lt;/td&gt;
&lt;td&gt;1 project, 5 deploys/day&lt;/td&gt;
&lt;td&gt;From $4/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bitbucket Pipelines&lt;/td&gt;
&lt;td&gt;50 min/month&lt;/td&gt;
&lt;td&gt;$15/month for 2,500 min&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A small team can run a complete CI/CD pipeline for under $20/month. Start there. Add complexity — and cost — only when you have evidence that you need it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;A good CI/CD pipeline does not have to be complicated. Start with automated tests on every push, a single staging environment, and a reliable deployment tool. Add deployment strategies, security scanning, and performance optimization as your team and application grow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;Sign up for \DeployHQ&lt;/a&gt; to handle the deployment side of your pipeline. Connect your repository, configure your server, and deploy with confidence.&lt;/p&gt;




&lt;p&gt;If you have questions about setting up your pipeline or need help with your deployment configuration, reach out to us at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or find us on Twitter at &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;@deployhq&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devopsinfrastructure</category>
      <category>tutorials</category>
      <category>cicd</category>
      <category>deployhq</category>
    </item>
    <item>
      <title>Best Software Deployment Tools in 2026</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Fri, 13 Mar 2026 11:38:59 +0000</pubDate>
      <link>https://dev.to/deployhq/best-software-deployment-tools-in-2026-3g9o</link>
      <guid>https://dev.to/deployhq/best-software-deployment-tools-in-2026-3g9o</guid>
      <description>&lt;p&gt;Choosing a deployment tool is one of those decisions that shapes your entire workflow. Pick the wrong one and you'll spend more time fighting your tooling than shipping features.&lt;/p&gt;

&lt;p&gt;The problem is that &lt;q&gt;deployment tool&lt;/q&gt; means different things to different teams. A Rails developer deploying to a VPS needs something completely different from a platform team managing Kubernetes clusters. A freelancer shipping WordPress sites has different requirements to an enterprise running blue-green deployments across multiple regions.&lt;/p&gt;

&lt;p&gt;This guide compares the most popular software deployment tools in 2026, organized by what you're actually deploying to — so you can skip straight to the category that fits your setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we evaluated these tools
&lt;/h2&gt;

&lt;p&gt;We assessed each tool across five criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Setup complexity&lt;/strong&gt; : How long does it take to go from zero to first deployment?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment targets&lt;/strong&gt; : What can you deploy to? (Servers, containers, CDN, PaaS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build support&lt;/strong&gt; : Can the tool run build commands before deployment?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rollback capability&lt;/strong&gt; : How quickly can you undo a bad deployment?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing&lt;/strong&gt; : What does it actually cost for a small team (1-5 developers)?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Deploys to&lt;/th&gt;
&lt;th&gt;Build pipeline&lt;/th&gt;
&lt;th&gt;Free tier&lt;/th&gt;
&lt;th&gt;Starting price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Teams deploying to their own servers&lt;/td&gt;
&lt;td&gt;SSH, SFTP, S3, DigitalOcean Spaces&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;1 project&lt;/td&gt;
&lt;td&gt;$4/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Octopus Deploy&lt;/td&gt;
&lt;td&gt;Enterprise CD with complex release orchestration&lt;/td&gt;
&lt;td&gt;Servers, Kubernetes, cloud services&lt;/td&gt;
&lt;td&gt;Limited (CI separate)&lt;/td&gt;
&lt;td&gt;10 targets&lt;/td&gt;
&lt;td&gt;$13/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS CodeDeploy&lt;/td&gt;
&lt;td&gt;AWS-native deployments&lt;/td&gt;
&lt;td&gt;EC2, ECS, Lambda&lt;/td&gt;
&lt;td&gt;No (use CodeBuild)&lt;/td&gt;
&lt;td&gt;Free with AWS&lt;/td&gt;
&lt;td&gt;AWS costs only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Netlify&lt;/td&gt;
&lt;td&gt;Static sites and Jamstack&lt;/td&gt;
&lt;td&gt;Netlify CDN&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;100GB bandwidth&lt;/td&gt;
&lt;td&gt;$19/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vercel&lt;/td&gt;
&lt;td&gt;Next.js and frontend frameworks&lt;/td&gt;
&lt;td&gt;Vercel Edge Network&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;100GB bandwidth&lt;/td&gt;
&lt;td&gt;$20/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Railway&lt;/td&gt;
&lt;td&gt;Full-stack apps on managed infra&lt;/td&gt;
&lt;td&gt;Railway containers&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;$5 credit&lt;/td&gt;
&lt;td&gt;$5/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Actions&lt;/td&gt;
&lt;td&gt;CI/CD tightly integrated with GitHub&lt;/td&gt;
&lt;td&gt;Anywhere (via scripts)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;2,000 min/mo&lt;/td&gt;
&lt;td&gt;$4/user/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Argo CD&lt;/td&gt;
&lt;td&gt;GitOps for Kubernetes&lt;/td&gt;
&lt;td&gt;Kubernetes clusters&lt;/td&gt;
&lt;td&gt;No (CI separate)&lt;/td&gt;
&lt;td&gt;Free (open source)&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Best for deploying to your own servers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  DeployHQ
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; is a dedicated deployment tool built specifically for teams that deploy to servers they control — VPS instances, cloud servers, shared hosting, or on-premise hardware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt; : Connect your Git repository (GitHub, GitLab, Bitbucket, or any Git remote), configure your server connection (SSH, SFTP, FTP, or cloud storage), and &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles the rest. When you push to a designated branch, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; runs your &lt;a href="https://www.deployhq.com/features/build-pipelines" rel="noopener noreferrer"&gt;build commands&lt;/a&gt;, transfers only the changed files, and executes any post-deployment scripts on your server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup takes minutes, not hours — connect repo, add server, deploy&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com/features/build-pipelines" rel="noopener noreferrer"&gt;Build pipelines&lt;/a&gt; run compilation and dependency installation on DeployHQ's servers&lt;/li&gt;
&lt;li&gt;Only transfers changed files (not full re-uploads), making deployments fast&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com/features/zero-downtime-deployments" rel="noopener noreferrer"&gt;Zero-downtime deployments&lt;/a&gt; via atomic symlink switching&lt;/li&gt;
&lt;li&gt;One-click rollback to any previous deployment&lt;/li&gt;
&lt;li&gt;Works with any server you can connect to over SSH or SFTP&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com/deploy-from-github" rel="noopener noreferrer"&gt;Deploy from GitHub&lt;/a&gt;, &lt;a href="https://www.deployhq.com/deploy-from-gitlab" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;, or Bitbucket with automatic triggers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Not designed for container orchestration (Docker, Kubernetes)&lt;/li&gt;
&lt;li&gt;No built-in CI (pair with GitHub Actions or GitLab CI for testing)&lt;/li&gt;
&lt;li&gt;UI-focused — less scriptable than CLI-first tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt; : Free for 1 project. Paid plans from $4/month for 5 projects. &lt;a href="https://www.deployhq.com/pricing" rel="noopener noreferrer"&gt;See pricing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt; : Web agencies, freelancers, and development teams deploying PHP, Ruby, Python, Node.js, or static sites to VPS or cloud servers. Especially strong for teams managing &lt;a href="https://www.deployhq.com/for-agencies" rel="noopener noreferrer"&gt;multiple client projects&lt;/a&gt; who need a simple, reliable deployment workflow without DevOps overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Octopus Deploy
&lt;/h3&gt;

&lt;p&gt;Octopus &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; is an enterprise-grade continuous delivery platform focused on release orchestration — managing complex deployments across multiple environments, approval workflows, and compliance requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt; : Octopus sits downstream of your CI server. Your CI pipeline (Jenkins, GitHub Actions, TeamCity) produces a package, and Octopus handles deploying that package through your environments (dev → staging → production) with configurable approval gates, variable management, and runbooks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Powerful release orchestration with multi-environment promotion&lt;/li&gt;
&lt;li&gt;Deployment targets include servers, Kubernetes, Azure, AWS, and more&lt;/li&gt;
&lt;li&gt;Runbooks for operational tasks beyond deployment&lt;/li&gt;
&lt;li&gt;Tenanted deployments for SaaS vendors deploying to multiple customers&lt;/li&gt;
&lt;li&gt;Strong audit logging and compliance features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex setup — expect hours to days for initial configuration&lt;/li&gt;
&lt;li&gt;Requires a separate CI tool (doesn't build or test code)&lt;/li&gt;
&lt;li&gt;Pricing scales with deployment targets, which can get expensive&lt;/li&gt;
&lt;li&gt;Overkill for small teams or simple deployment workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt; : Free for up to 10 deployment targets. Cloud plans from $13/month. Self-hosted available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt; : Enterprise teams with complex release processes, multiple environments, and compliance requirements. SaaS companies deploying to customer-specific infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best for AWS-native deployments
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AWS CodeDeploy
&lt;/h3&gt;

&lt;p&gt;AWS CodeDeploy automates deployments to EC2 instances, ECS containers, and Lambda functions. It's part of the broader AWS developer tools suite (CodePipeline, CodeBuild, CodeCommit).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt; : You define a deployment configuration (appspec.yml) that tells CodeDeploy how to stop the old version, install the new one, and start it up. CodeDeploy handles rolling deployments, blue-green deployments, and automatic rollback based on CloudWatch alarms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native integration with all AWS services&lt;/li&gt;
&lt;li&gt;Blue-green deployments for EC2 and ECS&lt;/li&gt;
&lt;li&gt;Automatic rollback on deployment failure or alarm triggers&lt;/li&gt;
&lt;li&gt;No additional cost (you pay only for the underlying AWS resources)&lt;/li&gt;
&lt;li&gt;Works with Auto Scaling groups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS-only — doesn't deploy to non-AWS infrastructure&lt;/li&gt;
&lt;li&gt;Requires significant AWS knowledge to configure&lt;/li&gt;
&lt;li&gt;No build capability (use CodeBuild or an external CI tool)&lt;/li&gt;
&lt;li&gt;Configuration via YAML and CLI — no visual deployment dashboard&lt;/li&gt;
&lt;li&gt;Steep learning curve compared to dedicated deployment tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt; : Free. You pay only for the AWS resources you use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt; : Teams already invested in the AWS ecosystem who want tight integration with EC2, ECS, and Lambda without adding another vendor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best for static sites and frontend frameworks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Netlify
&lt;/h3&gt;

&lt;p&gt;Netlify pioneered the Jamstack deployment model: push to Git, Netlify builds your site, and deploys it to a global CDN. It handles static sites, serverless functions, and edge computing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt; : Connect your repository, configure a build command (e.g., &lt;code&gt;npm run build&lt;/code&gt;), and every push triggers a new deployment. Netlify serves your site from its CDN with automatic HTTPS, form handling, and serverless functions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; previews for every pull request&lt;/li&gt;
&lt;li&gt;Instant rollback (every deployment is immutable)&lt;/li&gt;
&lt;li&gt;Built-in forms, identity, and serverless functions&lt;/li&gt;
&lt;li&gt;Split testing (A/B deployments)&lt;/li&gt;
&lt;li&gt;Global CDN with automatic HTTPS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built for static sites and Jamstack — not for traditional server-side applications&lt;/li&gt;
&lt;li&gt;Serverless functions have execution limits (10-second timeout on free tier)&lt;/li&gt;
&lt;li&gt;Bandwidth limits can surprise you on popular sites&lt;/li&gt;
&lt;li&gt;Vendor lock-in for Netlify-specific features (forms, identity)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt; : Free tier with 100GB bandwidth. Pro from $19/month per member.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt; : Static sites, Jamstack applications, and marketing sites built with frameworks like Gatsby, Hugo, Next.js (static export), or Eleventy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Vercel
&lt;/h3&gt;

&lt;p&gt;Vercel is the company behind Next.js, and their platform is optimized for deploying Next.js applications — though it supports other frameworks too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt; : Similar to Netlify — connect your repository, push code, and Vercel builds and deploys. The key difference is deep Next.js integration: server-side rendering, API routes, incremental static regeneration, and edge functions all work out of the box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Best-in-class Next.js support (server components, ISR, middleware)&lt;/li&gt;
&lt;li&gt;Edge functions for low-latency server-side logic&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; previews with comments for team collaboration&lt;/li&gt;
&lt;li&gt;Analytics and speed insights built in&lt;/li&gt;
&lt;li&gt;Excellent developer experience and documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optimized for Next.js — other frameworks get fewer features&lt;/li&gt;
&lt;li&gt;Pricing can spike with high traffic (bandwidth and function invocations)&lt;/li&gt;
&lt;li&gt;Not suitable for traditional backend applications&lt;/li&gt;
&lt;li&gt;Vendor lock-in for Vercel-specific features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt; : Free tier (hobby). Pro from $20/month per member.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt; : Next.js applications. If you're building with Next.js, Vercel is the path of least resistance. For other frameworks, evaluate against Netlify and Cloudflare Pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best for full-stack apps on managed infrastructure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Railway
&lt;/h3&gt;

&lt;p&gt;Railway provides a modern PaaS experience: deploy any application (Node.js, Python, Go, Rust, Docker) from a Git repository, and Railway handles the infrastructure — servers, databases, networking, and scaling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt; : Connect your repository or push a Docker image. Railway detects your framework, builds your application, and runs it on managed infrastructure. You can add databases (PostgreSQL, MySQL, Redis, MongoDB) with one click.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploys almost anything — not limited to static sites&lt;/li&gt;
&lt;li&gt;One-click databases and services&lt;/li&gt;
&lt;li&gt;Simple pricing based on usage (compute + memory)&lt;/li&gt;
&lt;li&gt;Good developer experience with CLI and dashboard&lt;/li&gt;
&lt;li&gt;Supports private networking between services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You don't control the underlying infrastructure&lt;/li&gt;
&lt;li&gt;Less mature than Heroku or Render for some edge cases&lt;/li&gt;
&lt;li&gt;Pricing can be unpredictable for compute-heavy applications&lt;/li&gt;
&lt;li&gt;Limited configuration for advanced networking or custom domains&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt; : $5 one-time credit on the free trial. Usage-based pricing starting from $5/month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt; : Side projects, startups, and small teams who want to deploy full-stack applications without managing servers. Good alternative to Heroku.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best for CI/CD pipelines
&lt;/h2&gt;

&lt;h3&gt;
  
  
  GitHub Actions
&lt;/h3&gt;

&lt;p&gt;GitHub Actions is a CI/CD platform built into GitHub. It can build, test, and deploy code triggered by any GitHub event (push, pull request, release, schedule).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt; : Define workflows in YAML files (&lt;code&gt;.github/workflows/&lt;/code&gt;). Each workflow consists of jobs that run on GitHub-hosted or self-hosted runners. You can deploy to any target by writing the appropriate deployment steps — or use pre-built actions from the GitHub Marketplace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integrated directly into GitHub — no separate tool to manage&lt;/li&gt;
&lt;li&gt;Massive marketplace of pre-built actions&lt;/li&gt;
&lt;li&gt;Matrix builds for testing across multiple versions/platforms&lt;/li&gt;
&lt;li&gt;Free for public repositories&lt;/li&gt;
&lt;li&gt;Self-hosted runners for private infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deployment is &lt;q&gt;roll your own&lt;/q&gt; — you write the scripts&lt;/li&gt;
&lt;li&gt;No deployment dashboard, release history, or one-click rollback&lt;/li&gt;
&lt;li&gt;YAML configuration can become complex for multi-environment deployments&lt;/li&gt;
&lt;li&gt;Not a deployment tool — it's a CI/CD platform that &lt;em&gt;can&lt;/em&gt; deploy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt; : Free for public repos (2,000 minutes/month for private). Team plan from $4/user/month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt; : Teams already on GitHub who want CI/CD without adding another vendor. Pairs well with a dedicated deployment tool — use GitHub Actions for CI (build + test) and &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; for CD (deployment to servers).&lt;/p&gt;

&lt;h3&gt;
  
  
  Argo CD
&lt;/h3&gt;

&lt;p&gt;Argo CD is an open-source, GitOps-based continuous delivery tool for Kubernetes. It watches a Git repository containing Kubernetes manifests and automatically syncs them to your cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt; : You define your desired cluster state in Git (Kubernetes YAML, Helm charts, Kustomize). Argo CD continuously compares the live cluster state to the Git state and can automatically reconcile differences — or alert you to drift.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitOps model — Git is the single source of truth&lt;/li&gt;
&lt;li&gt;Real-time visualization of Kubernetes resources&lt;/li&gt;
&lt;li&gt;Automatic drift detection and sync&lt;/li&gt;
&lt;li&gt;Multi-cluster support&lt;/li&gt;
&lt;li&gt;Open source and free&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Limitations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes-only — not for traditional server deployments&lt;/li&gt;
&lt;li&gt;Requires understanding of Kubernetes concepts&lt;/li&gt;
&lt;li&gt;No CI capability (pair with a CI tool for build and test)&lt;/li&gt;
&lt;li&gt;Complex initial setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt; : Free and open source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt; : Teams running Kubernetes who want a GitOps workflow. Not relevant if you're deploying to traditional servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to choose the right deployment tool
&lt;/h2&gt;

&lt;p&gt;The decision tree is simpler than most comparison articles make it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A[What are you deploying to?] --&amp;gt; B{Your own servers?}
    B --&amp;gt;|VPS, cloud, on-prem| C[DeployHQ]
    B --&amp;gt;|No| D{Kubernetes?}
    D --&amp;gt;|Yes| E[Argo CD + CI tool]
    D --&amp;gt;|No| F{Static site?}
    F --&amp;gt;|Yes| G{Next.js?}
    G --&amp;gt;|Yes| H[Vercel]
    G --&amp;gt;|No| I[Netlify or Cloudflare Pages]
    F --&amp;gt;|No| J{AWS only?}
    J --&amp;gt;|Yes| K[AWS CodeDeploy]
    J --&amp;gt;|No| L{Want managed infra?}
    L --&amp;gt;|Yes| M[Railway]
    L --&amp;gt;|No| N{Enterprise release orchestration?}
    N --&amp;gt;|Yes| O[Octopus Deploy]
    N --&amp;gt;|No| C

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key questions:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Do you manage your own servers?&lt;/strong&gt; If you deploy to VPS instances, cloud servers, or on-premise hardware via SSH/SFTP, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; gives you the simplest path to automated deployments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Are you running Kubernetes?&lt;/strong&gt; Use Argo CD or Flux for GitOps-based deployment. These tools are purpose-built for container orchestration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Is it a static site or Jamstack app?&lt;/strong&gt; Netlify, Vercel, or Cloudflare Pages. Pick based on your framework — Vercel for Next.js, Netlify or Cloudflare for everything else.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Do you want managed infrastructure?&lt;/strong&gt; Railway, Render, or Fly.io remove the need to manage servers entirely. Good for startups and side projects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Do you need enterprise release orchestration?&lt;/strong&gt; Octopus &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; handles complex multi-environment promotion, approval gates, and compliance workflows.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most teams deploying web applications to servers they control will get the most value from &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; — it's focused on doing one thing well: getting your code from Git to your server, reliably, with build pipelines and zero-downtime deployments built in.&lt;/p&gt;




&lt;p&gt;Ready to try it? &lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;Sign up for DeployHQ&lt;/a&gt; and deploy your first project in under 5 minutes — no credit card required.&lt;/p&gt;

&lt;p&gt;Questions? Reach out at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or on Twitter &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;@deployhq&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devopsinfrastructure</category>
      <category>whatis</category>
      <category>softwaredeploymenttools</category>
      <category>2026</category>
    </item>
    <item>
      <title>What Is Software Deployment? A Complete Guide</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Fri, 13 Mar 2026 11:38:53 +0000</pubDate>
      <link>https://dev.to/deployhq/what-is-software-deployment-a-complete-guide-53on</link>
      <guid>https://dev.to/deployhq/what-is-software-deployment-a-complete-guide-53on</guid>
      <description>&lt;p&gt;Software deployment is the process of moving code from a development environment to a place where it can be used — typically a production server. It's the bridge between writing code and making it available to users.&lt;/p&gt;

&lt;p&gt;That might sound straightforward, but in practice, deployment involves configuration, testing, coordination, and risk management. Understanding how deployment works — and how to do it well — is fundamental to shipping software reliably.&lt;/p&gt;

&lt;p&gt;This guide covers what software deployment means, the steps involved, common strategies, and how tools like &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; simplify the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does software deployment mean?
&lt;/h2&gt;

&lt;p&gt;At its core, software deployment is the act of taking code that has been written, tested, and approved, and placing it in an environment where it runs for its intended audience. That environment is usually a production server, but it can also be a staging server, a test environment, or a content delivery network.&lt;/p&gt;

&lt;p&gt;Deployment is not the same as development. Development is writing the code. Deployment is getting that code running somewhere.&lt;/p&gt;

&lt;p&gt;It's also not the same as a &lt;em&gt;release&lt;/em&gt;. A deployment is a technical event — moving code to a server. A release is a business event — making a feature available to users. You can deploy code without releasing it (for example, using &lt;a href="https://www.deployhq.com/blog/what-are-feature-flags" rel="noopener noreferrer"&gt;feature flags&lt;/a&gt; to keep new functionality hidden until you're ready). This distinction matters because it means you can deploy frequently and safely, decoupling technical risk from business timing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why software deployment matters
&lt;/h2&gt;

&lt;p&gt;Every line of code is worthless until it's running in production. Deployment is what turns development effort into user value, and how you handle it directly affects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reliability&lt;/strong&gt; : A poor deployment process is the single most common cause of production outages. Broken deployments take down websites, corrupt data, and erode user trust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt; : Teams that deploy frequently ship features faster, fix bugs sooner, and respond to market changes more quickly. The &lt;a href="https://dora.dev/research/2023/dora-report/" rel="noopener noreferrer"&gt;2023 DORA report&lt;/a&gt; found that elite teams deploy on demand — multiple times per day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer experience&lt;/strong&gt; : Manual, error-prone deployments create anxiety and slow teams down. Automated deployments free developers to focus on building rather than babysitting releases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business outcomes&lt;/strong&gt; : Faster, safer deployments mean shorter time-to-market, fewer incidents, and happier customers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The software deployment process
&lt;/h2&gt;

&lt;p&gt;While every team's workflow is different, most deployment processes follow the same general steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Code committed] --&amp;gt; B[Build]
    B --&amp;gt; C[Test]
    C --&amp;gt; D[Stage]
    D --&amp;gt; E[Deploy to production]
    E --&amp;gt; F[Verify &amp;amp; monitor]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Code is committed to version control
&lt;/h3&gt;

&lt;p&gt;Deployment starts with code being pushed to a &lt;a href="https://blog.deployhq.com/git/creating-a-repository" rel="noopener noreferrer"&gt;Git repository&lt;/a&gt;. This is the trigger — either a developer pushes to a specific branch (like &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;production&lt;/code&gt;), or a pull request is merged.&lt;/p&gt;

&lt;p&gt;Most modern deployment tools, including &lt;a href="https://www.deployhq.com/features/automatic-deployments" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;, can watch your repository and trigger deployments automatically when new commits land on a designated branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Build
&lt;/h3&gt;

&lt;p&gt;Before code can run on a server, it often needs to be compiled, bundled, or otherwise transformed. This build step might involve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compiling TypeScript to JavaScript&lt;/li&gt;
&lt;li&gt;Bundling frontend assets with Webpack or Vite&lt;/li&gt;
&lt;li&gt;Installing dependencies with &lt;code&gt;npm install&lt;/code&gt; or &lt;code&gt;composer install&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Running database migrations&lt;/li&gt;
&lt;li&gt;Generating static files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tools like &lt;a href="https://www.deployhq.com/features/build-pipelines" rel="noopener noreferrer"&gt;DeployHQ's build pipeline&lt;/a&gt; run these commands on a dedicated build server before the result is sent to your production server — keeping your deployment clean and consistent.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Test
&lt;/h3&gt;

&lt;p&gt;Automated tests should run before code reaches production. This includes unit tests, integration tests, and sometimes end-to-end tests. If tests fail, the deployment should stop.&lt;/p&gt;

&lt;p&gt;In a &lt;a href="https://blog.deployhq.com/blog/what-is-ci-cd" rel="noopener noreferrer"&gt;CI/CD pipeline&lt;/a&gt;, testing typically happens in a continuous integration (CI) service like GitHub Actions, GitLab CI, or CircleCI. The deployment tool then takes over for the continuous delivery (CD) part — pushing tested code to your servers.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Stage
&lt;/h3&gt;

&lt;p&gt;A staging environment is a replica of production where you verify that everything works as expected before going live. Staging catches issues that automated tests might miss — visual bugs, configuration problems, integration failures.&lt;/p&gt;

&lt;p&gt;Not every team uses staging. Smaller teams might deploy directly to production with safeguards like canary deployments or feature flags. But for applications where downtime is costly, a staging step is worth the effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Deploy to production
&lt;/h3&gt;

&lt;p&gt;This is the moment code reaches your live servers. Depending on your setup, this might involve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uploading files via SFTP or SSH&lt;/li&gt;
&lt;li&gt;Pulling the latest code from Git on the server&lt;/li&gt;
&lt;li&gt;Replacing a running container with an updated image&lt;/li&gt;
&lt;li&gt;Swapping traffic between server groups (blue-green deployment)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For teams deploying to their own servers — VPS instances on DigitalOcean, AWS EC2, Hetzner, or on-premise hardware — a tool like &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles the file transfer, runs post-deployment commands, and manages the entire workflow through a simple interface.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Verify and monitor
&lt;/h3&gt;

&lt;p&gt;Deployment doesn't end when files land on the server. You need to verify that the application is running correctly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Health checks confirm the application responds&lt;/li&gt;
&lt;li&gt;Error monitoring (Sentry, Bugsnag) catches exceptions&lt;/li&gt;
&lt;li&gt;Performance monitoring tracks response times&lt;/li&gt;
&lt;li&gt;Log aggregation reveals warnings or failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If something goes wrong, you need the ability to &lt;a href="https://www.deployhq.com/features/zero-downtime-deployments" rel="noopener noreferrer"&gt;roll back&lt;/a&gt; to the previous version quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Software deployment strategies
&lt;/h2&gt;

&lt;p&gt;Not all deployments work the same way. The strategy you choose depends on your risk tolerance, infrastructure, and how much downtime you can accept.&lt;/p&gt;

&lt;h3&gt;
  
  
  Direct deployment (replace and restart)
&lt;/h3&gt;

&lt;p&gt;The simplest approach: replace the old files with new ones and restart the application. This is what most small teams do, and it works well when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your application can tolerate a few seconds of downtime&lt;/li&gt;
&lt;li&gt;You have a single server&lt;/li&gt;
&lt;li&gt;Deployments are infrequent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The downside is that if something goes wrong, your site is down until you fix it or roll back.&lt;/p&gt;

&lt;h3&gt;
  
  
  Zero-downtime deployment
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com/features/zero-downtime-deployments" rel="noopener noreferrer"&gt;Zero-downtime deployment&lt;/a&gt; ensures users never see an error page during a release. The new version is prepared alongside the old one, and traffic is switched over only when the new version is ready.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; supports this through atomic deployments — uploading new files to a separate directory, then switching a symlink once the upload and build steps are complete.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blue-green deployment
&lt;/h3&gt;

&lt;p&gt;You maintain two identical production environments: &lt;q&gt;blue&lt;/q&gt; (current) and &lt;q&gt;green&lt;/q&gt; (new). You deploy to the inactive environment, test it, then switch traffic from blue to green. If anything goes wrong, you switch back instantly.&lt;/p&gt;

&lt;p&gt;This requires double the infrastructure but provides the fastest possible rollback.&lt;/p&gt;

&lt;h3&gt;
  
  
  Canary deployment
&lt;/h3&gt;

&lt;p&gt;Instead of deploying to all servers at once, you deploy to a small subset first (the &lt;q&gt;canary&lt;/q&gt;). You monitor that subset for errors, and if everything looks good, you gradually roll out to the rest. This limits the blast radius of a bad deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rolling deployment
&lt;/h3&gt;

&lt;p&gt;Similar to canary, but you update servers one at a time (or in small batches) until all servers are running the new version. At any point during the rollout, some servers run the old version and some run the new one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A[New version ready] --&amp;gt; B[Deploy to Server 1]
    B --&amp;gt; C{Healthy?}
    C --&amp;gt;|Yes| D[Deploy to Server 2]
    C --&amp;gt;|No| E[Roll back Server 1]
    D --&amp;gt; F{Healthy?}
    F --&amp;gt;|Yes| G[Deploy to Server 3]
    F --&amp;gt;|No| H[Roll back Servers 1-2]
    G --&amp;gt; I[All servers updated]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common deployment failures (and how to avoid them)
&lt;/h2&gt;

&lt;p&gt;Understanding why deployments fail helps you build a process that prevents them:&lt;/p&gt;

&lt;h3&gt;
  
  
  Missing dependencies
&lt;/h3&gt;

&lt;p&gt;The code works on your machine because you installed a package months ago. The production server doesn't have it. &lt;strong&gt;Fix&lt;/strong&gt; : Always install dependencies as part of your build step, and never rely on manually installed packages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment configuration drift
&lt;/h3&gt;

&lt;p&gt;Your staging server has different environment variables, a different database version, or a different OS version than production. &lt;strong&gt;Fix&lt;/strong&gt; : Use infrastructure as code and keep environments as identical as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Database migration failures
&lt;/h3&gt;

&lt;p&gt;A migration runs in the wrong order, or a migration assumes data exists that doesn't. &lt;strong&gt;Fix&lt;/strong&gt; : Test migrations against a copy of production data. Make migrations reversible where possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  File permission issues
&lt;/h3&gt;

&lt;p&gt;Uploaded files have the wrong permissions, so the web server can't read them or the application can't write to a cache directory. &lt;strong&gt;Fix&lt;/strong&gt; : Set file permissions explicitly in your deployment configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;q&gt;It worked in staging&lt;/q&gt;
&lt;/h3&gt;

&lt;p&gt;Staging doesn't perfectly mirror production — different traffic patterns, different data volumes, different third-party integrations. &lt;strong&gt;Fix&lt;/strong&gt; : Monitor closely after every production deployment and have a fast rollback plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where deployment fits in the development lifecycle
&lt;/h2&gt;

&lt;p&gt;Software deployment is one stage in a broader workflow. Here's how it connects to the rest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Plan] --&amp;gt; B[Develop]
    B --&amp;gt; C[Commit &amp;amp; push]
    C --&amp;gt; D[CI: Build &amp;amp; test]
    D --&amp;gt; E[CD: Deploy]
    E --&amp;gt; F[Monitor]
    F --&amp;gt;|Feedback| A

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plan&lt;/strong&gt; : Define what to build&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Develop&lt;/strong&gt; : Write the code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commit &amp;amp; push&lt;/strong&gt; : Save changes to &lt;a href="https://blog.deployhq.com/git/creating-a-repository" rel="noopener noreferrer"&gt;Git&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI (Continuous Integration)&lt;/strong&gt;: Automatically build and test on every push&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CD (Continuous Delivery/Deployment)&lt;/strong&gt;: Automatically deploy tested code to servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor&lt;/strong&gt; : Watch for issues, gather feedback, improve&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CI part is typically handled by services like GitHub Actions, GitLab CI, or Jenkins. The CD part — actually getting the code onto your servers — is where &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; fits. It connects to your repository, watches for changes, runs your build commands, and deploys the result to your servers over SSH, SFTP, or to cloud storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment automation vs manual deployment
&lt;/h2&gt;

&lt;p&gt;Manual deployment — SSHing into a server, pulling code, running commands by hand — works when you're just starting out. But it doesn't scale:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Manual deployment&lt;/th&gt;
&lt;th&gt;Automated deployment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consistency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Depends on who runs it&lt;/td&gt;
&lt;td&gt;Same process every time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minutes to hours&lt;/td&gt;
&lt;td&gt;Seconds to minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Error rate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (human error)&lt;/td&gt;
&lt;td&gt;Low (scripted)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Audit trail&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None unless you document it&lt;/td&gt;
&lt;td&gt;Automatic logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Frequency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Discouraged (too risky)&lt;/td&gt;
&lt;td&gt;Encouraged (low risk)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rollback&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual and stressful&lt;/td&gt;
&lt;td&gt;One-click or automatic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://blog.deployhq.com/blog/deployment-automation-a-quick-overview" rel="noopener noreferrer"&gt;Deployment automation&lt;/a&gt; removes the human from the process, making deployments faster, safer, and more frequent. This is what enables teams to deploy daily — or even multiple times per day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a deployment approach
&lt;/h2&gt;

&lt;p&gt;The right deployment setup depends on your situation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploying to your own servers (VPS, cloud, on-premise)?&lt;/strong&gt;You need a tool that connects to your server over SSH or SFTP and handles file transfer, build steps, and configuration. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; is built for exactly this — it works with any server you can connect to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using a Platform-as-a-Service (PaaS)?&lt;/strong&gt;Platforms like Heroku, Railway, or Render handle deployment as part of their service. You push code, they build and deploy it. Less control, but less setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Running containers?&lt;/strong&gt; Container-based deployments (Docker, Kubernetes) package your application and its dependencies together. You build a container image, push it to a registry, and orchestrate deployment across your infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Static sites?&lt;/strong&gt; Static site generators (Next.js, Hugo, Jekyll) can deploy to CDNs like Cloudflare Pages, Netlify, or Vercel. Build once, serve everywhere.&lt;/p&gt;

&lt;p&gt;For many teams — especially those deploying web applications to their own servers — a tool like &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; provides the right balance of automation, control, and simplicity without the complexity of container orchestration or the lock-in of a PaaS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Software deployment checklist
&lt;/h2&gt;

&lt;p&gt;Before deploying to production, verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[] All tests pass in CI&lt;/li&gt;
&lt;li&gt;[] Build completes without errors&lt;/li&gt;
&lt;li&gt;[] Environment variables are configured correctly&lt;/li&gt;
&lt;li&gt;[] Database migrations have been tested&lt;/li&gt;
&lt;li&gt;[] Staging deployment has been verified (if applicable)&lt;/li&gt;
&lt;li&gt;[] Rollback plan is in place&lt;/li&gt;
&lt;li&gt;[] Team is aware of the deployment&lt;/li&gt;
&lt;li&gt;[] Monitoring and alerting are active&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a more detailed checklist, see our &lt;a href="https://dev.to/deployhq/the-ultimate-deployment-checklist-ensuring-smooth-and-successful-releases-47lm"&gt;ultimate deployment checklist&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started with software deployment
&lt;/h2&gt;

&lt;p&gt;If you're deploying for the first time, start simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Get your code into Git&lt;/strong&gt; — if it's not in a &lt;a href="https://blog.deployhq.com/git/creating-a-repository" rel="noopener noreferrer"&gt;Git repository&lt;/a&gt; yet, start there&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose a server&lt;/strong&gt; — a VPS from DigitalOcean, Hetzner, or AWS is a good starting point&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up a deployment tool&lt;/strong&gt; — &lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;sign up for DeployHQ&lt;/a&gt;, connect your repository, and configure your server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy&lt;/strong&gt; — push to your deployment branch and watch it go live&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automate&lt;/strong&gt; — enable automatic deployments so every push triggers a deploy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From there, you can layer on build pipelines, staging environments, zero-downtime deployments, and monitoring as your application grows.&lt;/p&gt;




&lt;p&gt;Ready to simplify your deployment workflow? &lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; connects to your Git repository and deploys to any server over SSH, SFTP, or to cloud storage — with build pipelines, zero-downtime deployments, and one-click rollbacks built in.&lt;/p&gt;

&lt;p&gt;Questions? Reach out at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or on Twitter &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;@deployhq&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devopsinfrastructure</category>
      <category>whatis</category>
      <category>softwaredeployment</category>
      <category>deployhq</category>
    </item>
    <item>
      <title>CLAUDE.md, AGENTS.md, and Every AI Config File Explained</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Wed, 11 Mar 2026 10:07:28 +0000</pubDate>
      <link>https://dev.to/deployhq/claudemd-agentsmd-and-every-ai-config-file-explained-4pde</link>
      <guid>https://dev.to/deployhq/claudemd-agentsmd-and-every-ai-config-file-explained-4pde</guid>
      <description>&lt;p&gt;Every AI coding tool now reads a configuration file from your project. &lt;a href="https://dev.to/deployhq/getting-started-with-claude-code-the-ai-coding-assistant-for-your-terminal-4cba"&gt;Claude Code&lt;/a&gt; looks for &lt;code&gt;CLAUDE.md&lt;/code&gt;. &lt;a href="https://dev.to/deployhq/getting-started-with-openai-codex-cli-ai-powered-code-generation-from-your-terminal-5hm8"&gt;Codex CLI&lt;/a&gt; reads &lt;code&gt;AGENTS.md&lt;/code&gt;. &lt;a href="https://dev.to/deployhq/getting-started-with-google-gemini-cli-open-source-ai-agent-for-your-terminal-25e1"&gt;Gemini CLI&lt;/a&gt; checks for &lt;code&gt;GEMINI.md&lt;/code&gt;. &lt;a href="https://www.deployhq.com/guides/cursor" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; has &lt;code&gt;.cursorrules&lt;/code&gt;. GitHub Copilot uses &lt;code&gt;copilot-instructions.md&lt;/code&gt;. &lt;a href="https://www.deployhq.com/guides/windsurf" rel="noopener noreferrer"&gt;Windsurf&lt;/a&gt; has &lt;code&gt;.windsurfrules&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These files solve a fundamental problem: AI models have no persistent memory. Every session starts blank. Without a configuration file, you'd repeat the same instructions every time — your tech stack, coding conventions, project structure, deployment rules. These files give your AI assistant the context it needs to produce useful code from the first prompt.&lt;/p&gt;

&lt;p&gt;This guide covers every major format, explains how they work, and shows you how to write effective instructions that actually improve your AI's output.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Complete Landscape
&lt;/h2&gt;

&lt;p&gt;Here's every AI configuration file format in use today:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;Project root + &lt;code&gt;~/.claude/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Markdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Codex CLI, Cursor, Claude Code (fallback)&lt;/td&gt;
&lt;td&gt;Project root + subdirectories&lt;/td&gt;
&lt;td&gt;Markdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GEMINI.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Gemini CLI&lt;/td&gt;
&lt;td&gt;Project root + &lt;code&gt;~/.gemini/&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Markdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.cursorrules&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cursor (legacy)&lt;/td&gt;
&lt;td&gt;Project root&lt;/td&gt;
&lt;td&gt;Plain text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.cursor/rules/*.mdc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cursor (current)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.cursor/rules/&lt;/code&gt; directory&lt;/td&gt;
&lt;td&gt;MDC (Markdown+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.github/copilot-instructions.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GitHub Copilot&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.github/&lt;/code&gt; directory&lt;/td&gt;
&lt;td&gt;Markdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.github/instructions/*.instructions.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GitHub Copilot (scoped)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.github/instructions/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Markdown + frontmatter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.windsurfrules&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Windsurf (legacy)&lt;/td&gt;
&lt;td&gt;Project root&lt;/td&gt;
&lt;td&gt;Plain text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.windsurf/rules/*.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Windsurf (current)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.windsurf/rules/&lt;/code&gt; directory&lt;/td&gt;
&lt;td&gt;Markdown&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every tool has converged on the same core idea: a markdown file in your repository that the AI reads before doing anything. The differences are in naming and how they handle hierarchy.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Each Format Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  CLAUDE.md — Claude Code
&lt;/h3&gt;

&lt;p&gt;Claude Code reads &lt;code&gt;CLAUDE.md&lt;/code&gt; files from three locations, merged in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt;&lt;/strong&gt; — Your personal global instructions (applied to all projects)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;./CLAUDE.md&lt;/code&gt;&lt;/strong&gt; — Project root instructions (shared with your team via git)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subdirectory &lt;code&gt;CLAUDE.md&lt;/code&gt; files&lt;/strong&gt; — Scoped instructions for specific parts of the codebase&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Claude Code also reads &lt;code&gt;AGENTS.md&lt;/code&gt; as a fallback if no &lt;code&gt;CLAUDE.md&lt;/code&gt; is found in a directory. This means if your team uses multiple AI tools, you can maintain a single &lt;code&gt;AGENTS.md&lt;/code&gt; and it will work with Claude Code automatically.&lt;/p&gt;

&lt;p&gt;A key constraint: research suggests frontier LLMs can reliably follow around 150-200 instructions. Claude Code's system prompt already uses about 50 of those, so keep your &lt;code&gt;CLAUDE.md&lt;/code&gt; concise — ideally under 300 lines.&lt;/p&gt;

&lt;h3&gt;
  
  
  AGENTS.md — The Cross-Tool Standard
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;AGENTS.md&lt;/code&gt; is the closest thing to a universal standard. Originally popularised by OpenAI's Codex CLI, it's now an &lt;a href="https://agents.md/" rel="noopener noreferrer"&gt;open format stewarded by the Linux Foundation&lt;/a&gt; with adoption across 60,000+ open-source projects. It's supported by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Codex CLI&lt;/strong&gt; (primary config file)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cursor&lt;/strong&gt; (reads alongside &lt;code&gt;.cursorrules&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code&lt;/strong&gt; (fallback when no &lt;code&gt;CLAUDE.md&lt;/code&gt; exists)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continue.dev&lt;/strong&gt; , &lt;strong&gt;Aider&lt;/strong&gt; , &lt;strong&gt;OpenHands&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Codex CLI has the most sophisticated discovery process. It walks from your project root down to the current working directory, checking each level for &lt;code&gt;AGENTS.md&lt;/code&gt; (or &lt;code&gt;AGENTS.override.md&lt;/code&gt; for local overrides). You can configure fallback filenames and size limits in &lt;code&gt;~/.codex/config.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;project_doc_fallback_filenames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"TEAM_GUIDE.md"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".agents.md"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;project_doc_max_bytes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;65536&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  GEMINI.md — Gemini CLI
&lt;/h3&gt;

&lt;p&gt;Gemini CLI uses &lt;code&gt;GEMINI.md&lt;/code&gt; with a hierarchical discovery system similar to Codex:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;~/.gemini/GEMINI.md&lt;/code&gt;&lt;/strong&gt; — Global defaults for all projects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workspace root &lt;code&gt;GEMINI.md&lt;/code&gt;&lt;/strong&gt; — Project-level instructions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subdirectory &lt;code&gt;GEMINI.md&lt;/code&gt; files&lt;/strong&gt; — Discovered dynamically when tools access files in those directories&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A unique feature: you can inspect the loaded context at any time with the &lt;code&gt;/memory show&lt;/code&gt; command, and force a reload with &lt;code&gt;/memory refresh&lt;/code&gt;. The filename itself is configurable via &lt;code&gt;settings.json&lt;/code&gt; if you prefer a different name.&lt;/p&gt;

&lt;h3&gt;
  
  
  .cursorrules / .cursor/rules/ — Cursor
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com/guides/cursor" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; has evolved through two formats:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Legacy&lt;/strong&gt; : A single &lt;code&gt;.cursorrules&lt;/code&gt; file in the project root. Still supported but deprecated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Current (recommended)&lt;/strong&gt;: A &lt;code&gt;.cursor/rules/&lt;/code&gt; directory containing multiple &lt;code&gt;.mdc&lt;/code&gt; files, each with its own scope:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always On&lt;/strong&gt; — Applied to every interaction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto Attached&lt;/strong&gt; — Activated when matching files are open&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model Decision&lt;/strong&gt; — The AI decides whether to apply based on a description&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual&lt;/strong&gt; — Only when explicitly mentioned via &lt;code&gt;@&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the most granular system — you can have &lt;code&gt;frontend.mdc&lt;/code&gt; for React conventions and &lt;code&gt;backend.mdc&lt;/code&gt; for API patterns, each activated only when relevant. Create rules quickly with the &lt;code&gt;/rules&lt;/code&gt; slash command in Cursor's terminal.&lt;/p&gt;

&lt;h3&gt;
  
  
  copilot-instructions.md — GitHub Copilot
&lt;/h3&gt;

&lt;p&gt;GitHub Copilot reads from &lt;code&gt;.github/copilot-instructions.md&lt;/code&gt; for project-wide instructions. Since July 2025, it also supports scoped instructions via &lt;code&gt;.github/instructions/*.instructions.md&lt;/code&gt; files with glob-pattern frontmatter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;applyTo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/*.tsx"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="s"&gt;Use functional components with TypeScript interfaces.&lt;/span&gt;
&lt;span class="s"&gt;Always use server components unless client interactivity is required.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets you define different rules for different file types — TypeScript vs Python, frontend vs backend — without cluttering a single file.&lt;/p&gt;

&lt;h3&gt;
  
  
  .windsurfrules / .windsurf/rules/ — Windsurf
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com/guides/windsurf" rel="noopener noreferrer"&gt;Windsurf&lt;/a&gt; follows the same legacy-to-directory evolution as Cursor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Legacy&lt;/strong&gt; : &lt;code&gt;.windsurfrules&lt;/code&gt; in the project root&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Current&lt;/strong&gt; : &lt;code&gt;.windsurf/rules/&lt;/code&gt; directory with individual rule files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rules can be &lt;strong&gt;Always On&lt;/strong&gt; , &lt;strong&gt;Manual&lt;/strong&gt; (via @mention), or &lt;strong&gt;Model Decision&lt;/strong&gt;. Note the character limits: individual rule files are capped at 6,000 characters, and total combined rules must not exceed 12,000 characters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which One Should You Use?
&lt;/h2&gt;

&lt;p&gt;If your team uses a single AI tool, use that tool's native format. But most teams now use multiple tools — Cursor in the IDE, Claude Code in the terminal, Copilot for quick completions. Here's a practical approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your-project/
├── AGENTS.md ← Universal instructions (works with Codex, Cursor, Claude Code)
├── CLAUDE.md ← Claude-specific additions (if needed)
├── .github/
│ └── copilot-instructions.md ← Copilot-specific (if your team uses it)
├── .cursor/
│ └── rules/ ← Cursor-specific scoped rules (if needed)
└── ...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Start with &lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/strong&gt; as your single source of truth. It has the widest cross-tool support. Then add tool-specific files only when you need features that &lt;code&gt;AGENTS.md&lt;/code&gt; can't provide (like Cursor's scoped activation or Copilot's glob patterns).&lt;/p&gt;

&lt;p&gt;For Claude Code users: you can make your &lt;code&gt;CLAUDE.md&lt;/code&gt; simply reference the shared instructions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Strictly follow the rules in ./AGENTS.md

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Writing Effective Instructions
&lt;/h2&gt;

&lt;p&gt;The most common mistake is treating these files like comprehensive documentation. They're not — they're concise instructions that fit within a model's attention window. Here's what works.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to Include
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Build and test commands&lt;/strong&gt; — The single most valuable thing you can provide:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Build: &lt;span class="sb"&gt;`npm run build`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Test single file: &lt;span class="sb"&gt;`npm test -- path/to/test.ts`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Lint: &lt;span class="sb"&gt;`npm run lint`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Type check: &lt;span class="sb"&gt;`npx tsc --noEmit`&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tech stack and versions&lt;/strong&gt; — Prevents the AI from guessing wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Stack&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Framework: Next.js 15 (App Router)
&lt;span class="p"&gt;-&lt;/span&gt; Language: TypeScript 5.7 (strict mode)
&lt;span class="p"&gt;-&lt;/span&gt; Styling: Tailwind CSS 4.0
&lt;span class="p"&gt;-&lt;/span&gt; Database: PostgreSQL 16 with Drizzle ORM
&lt;span class="p"&gt;-&lt;/span&gt; Deployment: DeployHQ → Ubuntu 22.04 VPS

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Project structure&lt;/strong&gt; — Especially important for monorepos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Structure&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`apps/web/`&lt;/span&gt; — Next.js frontend
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`apps/api/`&lt;/span&gt; — Express API server
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`packages/shared/`&lt;/span&gt; — Shared types and utilities
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`infra/`&lt;/span&gt; — Terraform configuration

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical conventions&lt;/strong&gt; — Things the AI would otherwise get wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Conventions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use named exports, not default exports
&lt;span class="p"&gt;-&lt;/span&gt; API routes return { data, error } shape
&lt;span class="p"&gt;-&lt;/span&gt; Database migrations go in &lt;span class="sb"&gt;`drizzle/migrations/`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Environment variables are validated in &lt;span class="sb"&gt;`src/env.ts`&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What to Leave Out
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Code style rules&lt;/strong&gt; — Use ESLint, Prettier, and formatters instead. They're faster, deterministic, and don't consume your instruction budget.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Obvious things&lt;/strong&gt; — &lt;q&gt;Write clean code&lt;/q&gt; and &lt;q&gt;follow best practices&lt;/q&gt; waste tokens. Be specific or don't include it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full API documentation&lt;/strong&gt; — Link to it instead. Use a &lt;code&gt;docs/&lt;/code&gt; or &lt;code&gt;agent_docs/&lt;/code&gt; directory for supplementary context that the AI can pull in when needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Task-specific instructions&lt;/strong&gt; — These belong in your prompt, not in a persistent config file.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Deployment-Focused Example
&lt;/h2&gt;

&lt;p&gt;Here's a complete &lt;code&gt;AGENTS.md&lt;/code&gt; for a PHP team using &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Project: Acme Web App&lt;/span&gt;

Laravel 11 application deployed via DeployHQ to Ubuntu 22.04 VPS.
Production: Nginx + PHP-FPM 8.3. Database: MySQL 8.

&lt;span class="gu"&gt;## Commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Run tests: &lt;span class="sb"&gt;`php artisan test --filter=TestName`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Run all tests: &lt;span class="sb"&gt;`php artisan test`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Lint: &lt;span class="sb"&gt;`./vendor/bin/pint`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Static analysis: &lt;span class="sb"&gt;`./vendor/bin/phpstan analyse`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; DB migrate: &lt;span class="sb"&gt;`php artisan migrate`&lt;/span&gt;

&lt;span class="gu"&gt;## Deployment Rules&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Never modify production configs directly
&lt;span class="p"&gt;-&lt;/span&gt; All database migrations must be reversible
&lt;span class="p"&gt;-&lt;/span&gt; Run migrations before deploying code that depends on schema changes
&lt;span class="p"&gt;-&lt;/span&gt; Include rollback steps in PR descriptions for risky changes
&lt;span class="p"&gt;-&lt;/span&gt; Feature flags for anything touching payments or auth
&lt;span class="p"&gt;-&lt;/span&gt; Run &lt;span class="sb"&gt;`composer install --no-dev`&lt;/span&gt; in production builds

&lt;span class="gu"&gt;## Structure&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`app/Services/`&lt;/span&gt; — Business logic (not in controllers)
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`app/Jobs/`&lt;/span&gt; — Background jobs (Laravel Queue)
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`app/Actions/`&lt;/span&gt; — Single-purpose action classes
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`deploy/`&lt;/span&gt; — DeployHQ build commands
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`tests/`&lt;/span&gt; — PHPUnit tests (mirror app/ structure)

&lt;span class="gu"&gt;## Conventions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Action classes: single &lt;span class="sb"&gt;`handle()`&lt;/span&gt; method
&lt;span class="p"&gt;-&lt;/span&gt; API responses: always use &lt;span class="sb"&gt;`JsonResource`&lt;/span&gt; classes
&lt;span class="p"&gt;-&lt;/span&gt; Logging: use &lt;span class="sb"&gt;`Log::info()`&lt;/span&gt;, never &lt;span class="sb"&gt;`dd()`&lt;/span&gt; or &lt;span class="sb"&gt;`dump()`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Secrets: use &lt;span class="sb"&gt;`config()`&lt;/span&gt; helper, never &lt;span class="sb"&gt;`env()`&lt;/span&gt; outside config files
&lt;span class="p"&gt;-&lt;/span&gt; Validation: FormRequest classes, never inline in controllers

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Writing a novel.&lt;/strong&gt; If your config file is over 500 lines, most of it is being ignored. LLMs have limited instruction-following capacity — a focused 50-line file outperforms a sprawling 1,000-line one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Duplicating across tools.&lt;/strong&gt; Maintain one source of truth (&lt;code&gt;AGENTS.md&lt;/code&gt;) and have tool-specific files reference it. Don't copy-paste the same rules into &lt;code&gt;CLAUDE.md&lt;/code&gt;, &lt;code&gt;.cursorrules&lt;/code&gt;, and &lt;code&gt;copilot-instructions.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;/init&lt;/code&gt; or auto-generators.&lt;/strong&gt; Auto-generated config files tend to be generic and bloated. Write yours by hand — every line should earn its place by solving a real problem you've encountered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Including things a linter handles.&lt;/strong&gt; &lt;q&gt;Use 2-space indentation&lt;/q&gt; and &lt;q&gt;always add trailing commas&lt;/q&gt; are Prettier's job, not your AI config file's job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting to update.&lt;/strong&gt; These files rot just like any other documentation. When your stack changes, update the instructions. A wrong instruction is worse than no instruction.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Looking to automate your deployment pipeline? &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; deploys code from Git to your servers automatically — one less thing to configure. &lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;Get started free&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Have questions? Reach out at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or find us on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;X/Twitter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tipstricks</category>
      <category>claude</category>
      <category>agents</category>
    </item>
    <item>
      <title>How to Deploy Laravel: Zero Downtime, Build Pipelines, and Best Practices</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Mon, 09 Mar 2026 06:12:29 +0000</pubDate>
      <link>https://dev.to/deployhq/how-to-deploy-laravel-zero-downtime-build-pipelines-and-best-practices-148j</link>
      <guid>https://dev.to/deployhq/how-to-deploy-laravel-zero-downtime-build-pipelines-and-best-practices-148j</guid>
      <description>&lt;p&gt;Laravel is the most widely used PHP framework, but deploying it involves more than copying files to a server. A typical Laravel deployment requires installing Composer dependencies, compiling frontend assets with Vite, running database migrations, clearing caches, restarting queue workers, and managing environment variables — all without interrupting users.&lt;/p&gt;

&lt;p&gt;Get any of those steps wrong and you end up with a white screen, missing assets, or a queue processing stale code.&lt;/p&gt;

&lt;p&gt;This guide covers the full Laravel deployment lifecycle: what the build process actually involves, how to choose between deployment tools, how to achieve zero-downtime releases, and the most common deployment failures and how to fix them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Laravel Deployment Actually Involves
&lt;/h2&gt;

&lt;p&gt;Laravel is not a static site you can deploy by uploading files. A production-ready deployment typically runs through these steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Build Process
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Install PHP dependencies (without dev packages)&lt;/span&gt;
composer &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-dev&lt;/span&gt; &lt;span class="nt"&gt;--optimize-autoloader&lt;/span&gt; &lt;span class="nt"&gt;--no-interaction&lt;/span&gt;

&lt;span class="c"&gt;# 2. Install and build frontend assets&lt;/span&gt;
npm ci
npm run build

&lt;span class="c"&gt;# 3. Cache configuration, routes, views, and events&lt;/span&gt;
php artisan optimize

&lt;span class="c"&gt;# 4. Run database migrations&lt;/span&gt;
php artisan migrate &lt;span class="nt"&gt;--force&lt;/span&gt;

&lt;span class="c"&gt;# 5. Create the storage symlink&lt;/span&gt;
php artisan storage:link

&lt;span class="c"&gt;# 6. Restart queue workers (so they pick up new code)&lt;/span&gt;
php artisan queue:restart

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each step matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--no-dev&lt;/code&gt;&lt;/strong&gt; excludes testing packages from production, reducing the vendor directory size&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/strong&gt; compiles your Vite or Mix assets into the &lt;code&gt;public/build/&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;php artisan optimize&lt;/code&gt;&lt;/strong&gt; is the unified caching command introduced in Laravel 11 — it replaces the separate &lt;code&gt;config:cache&lt;/code&gt;, &lt;code&gt;route:cache&lt;/code&gt;, &lt;code&gt;view:cache&lt;/code&gt;, and &lt;code&gt;event:cache&lt;/code&gt; commands&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;--force&lt;/code&gt;&lt;/strong&gt; is required for migrations in production (Laravel refuses to run migrations without it outside local environments)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;queue:restart&lt;/code&gt;&lt;/strong&gt; signals long-running queue workers to finish their current job and restart, picking up the new code&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Server Requirements for Laravel 12
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;PHP 8.2 or higher&lt;/li&gt;
&lt;li&gt;Required extensions: Ctype, cURL, DOM, Fileinfo, Filter, Hash, Mbstring, OpenSSL, PCRE, PDO, Session, Tokenizer, XML&lt;/li&gt;
&lt;li&gt;Web server (Nginx or Apache) configured to serve from the &lt;code&gt;public/&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;Database: MySQL 8.0+, PostgreSQL 10+, SQLite 3.35+, or SQL Server 2017+&lt;/li&gt;
&lt;li&gt;Write permissions on &lt;code&gt;storage/&lt;/code&gt; and &lt;code&gt;bootstrap/cache/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Environment Configuration
&lt;/h3&gt;

&lt;p&gt;Laravel uses a &lt;code&gt;.env&lt;/code&gt; file for environment-specific settings. This file must never be committed to version control — it contains your application key, database credentials, queue and cache driver configuration, and third-party API keys.&lt;/p&gt;

&lt;p&gt;Key production settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;APP_ENV&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;
&lt;span class="py"&gt;APP_DEBUG&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;
&lt;span class="py"&gt;APP_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;base64:your-generated-key&lt;/span&gt;
&lt;span class="py"&gt;APP_URL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://yourdomain.com&lt;/span&gt;

&lt;span class="py"&gt;DB_CONNECTION&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;mysql&lt;/span&gt;
&lt;span class="py"&gt;DB_HOST&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1&lt;/span&gt;
&lt;span class="py"&gt;DB_DATABASE&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your_database&lt;/span&gt;
&lt;span class="py"&gt;DB_USERNAME&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your_user&lt;/span&gt;
&lt;span class="py"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your_password&lt;/span&gt;

&lt;span class="py"&gt;CACHE_DRIVER&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;redis&lt;/span&gt;
&lt;span class="py"&gt;QUEUE_CONNECTION&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;redis&lt;/span&gt;
&lt;span class="py"&gt;SESSION_DRIVER&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;redis&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting &lt;code&gt;APP_DEBUG=false&lt;/code&gt; is critical — with debug mode enabled, Laravel exposes full stack traces, environment variables, and database credentials to anyone who triggers an error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing Your Deployment Strategy
&lt;/h2&gt;

&lt;p&gt;There are several ways to deploy Laravel, from manual SSH commands to fully managed platforms. The right choice depends on whether you already have servers, how much infrastructure you want to manage, and what other tools you use alongside Laravel.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparison Table
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Manual SSH&lt;/th&gt;
&lt;th&gt;Laravel Forge&lt;/th&gt;
&lt;th&gt;Laravel Cloud&lt;/th&gt;
&lt;th&gt;DeployHQ&lt;/th&gt;
&lt;th&gt;Deployer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server provisioning&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (managed)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build pipeline&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Basic scripts&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (remote servers)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zero-downtime releases&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (Envoyer add-on)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (atomic symlinks)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One-click rollback&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-server deploy&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Non-PHP project support&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Limited (Node, etc.)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (any stack)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web dashboard&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (CLI only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open source&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;$12-39/mo&lt;/td&gt;
&lt;td&gt;Usage-based&lt;/td&gt;
&lt;td&gt;€9-99/mo&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Manual Deployment (SSH + Git Pull)
&lt;/h3&gt;

&lt;p&gt;The simplest approach: SSH into your server, pull the latest code, and run the build commands manually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh user@yourserver.com
&lt;span class="nb"&gt;cd&lt;/span&gt; /var/www/myapp
git pull origin main
composer &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-dev&lt;/span&gt; &lt;span class="nt"&gt;--optimize-autoloader&lt;/span&gt;
npm ci &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build
php artisan migrate &lt;span class="nt"&gt;--force&lt;/span&gt;
php artisan optimize
php artisan queue:restart

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When it works&lt;/strong&gt; : Solo developer, single server, deploying infrequently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When it breaks&lt;/strong&gt; : Teams (who ran the commands last?), multiple servers (repeat everything N times), urgent rollbacks (which files changed?), and Friday afternoon deployments (one wrong command and you are debugging into the evening).&lt;/p&gt;

&lt;h3&gt;
  
  
  Laravel Forge
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://forge.laravel.com" rel="noopener noreferrer"&gt;Forge&lt;/a&gt; provisions and manages servers on DigitalOcean, Hetzner, AWS, or other providers. It handles Nginx configuration, SSL, database setup, and push-to-deploy with customisable deploy scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt; : Teams that want server management bundled with deployment. Forge handles everything from provisioning to SSL renewal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitations&lt;/strong&gt; : &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; scripts run on the production server (no separate build pipeline). Zero-downtime requires the separate &lt;a href="https://envoyer.io" rel="noopener noreferrer"&gt;Envoyer&lt;/a&gt; product ($12/mo extra). Limited to PHP/Node stacks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Laravel Cloud
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://cloud.laravel.com" rel="noopener noreferrer"&gt;Laravel Cloud&lt;/a&gt; is fully managed hosting — zero server management, automatic scaling, push-to-deploy from GitHub, and SOC 2 compliance. Launched February 2025.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt; : Teams that want zero infrastructure management and are building exclusively with Laravel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitations&lt;/strong&gt; : Vendor lock-in to Laravel's infrastructure. Usage-based pricing that can be hard to predict. Less control over server configuration. Laravel-only.&lt;/p&gt;

&lt;h3&gt;
  
  
  DeployHQ
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; is a deployment tool that connects your Git repository to your servers. Build commands run on DeployHQ's infrastructure (not on your production server), and files are deployed via SSH, SFTP, FTP, or S3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt; : Teams that already have servers (from any provider) and want automated deployment with build pipelines, zero-downtime releases, and multi-server support. Also ideal when you deploy Laravel alongside other stacks (Node.js frontend, WordPress marketing site, Python microservice).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key advantage&lt;/strong&gt; : Build pipeline runs remotely. Your production server does not need Node.js or npm installed — &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; compiles your Vite assets and installs Composer dependencies on its build servers, then deploys only the production-ready files.&lt;/p&gt;

&lt;p&gt;For a step-by-step setup walkthrough, see &lt;a href="https://www.deployhq.com/blog/deploying-a-laravel-react-application-to-fortrabbit-using-deployhq" rel="noopener noreferrer"&gt;How to&lt;/a&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; Laravel with DeployHQ.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployer
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://deployer.org" rel="noopener noreferrer"&gt;Deployer&lt;/a&gt; is a free, open-source PHP deployment tool with a dedicated Laravel recipe that includes 40+ artisan tasks, zero-downtime via symlinks, parallel deployment, and rollbacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt; : Developers who want full control, are comfortable with CLI tools, and prefer open-source solutions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitations&lt;/strong&gt; : No web dashboard, no build pipeline (builds run on your machine or CI server), steeper learning curve.&lt;/p&gt;

&lt;p&gt;For a detailed comparison, see &lt;a href="https://www.deployhq.com/blog/deployhq-vs-deployer-a-comparative-analysis-of-automated-deployment-tools" rel="noopener noreferrer"&gt;DeployHQ vs Deployer&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forge + DeployHQ Together
&lt;/h3&gt;

&lt;p&gt;This combination is worth highlighting because it addresses a common misconception: Forge and &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; are not competitors. They solve different problems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Forge&lt;/strong&gt; manages your server: provisioning, Nginx config, SSL, database, PHP version, firewall rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeployHQ&lt;/strong&gt; manages your deployment pipeline: build steps, file transfer, zero-downtime release, rollback, notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using both means Forge handles the infrastructure while &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles the deployment workflow — with remote build pipelines, one-click rollback, multi-server deployment, and team notifications that Forge's built-in deploy scripts do not offer.&lt;/p&gt;

&lt;p&gt;For a detailed comparison, see &lt;a href="https://dev.to/deployhq/deployhq-vs-laravel-forge-how-they-differ-and-how-to-use-them-together-5e5n-temp-slug-4440760"&gt;DeployHQ vs Laravel Forge: How They Differ and How to Use Them Together&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero-Downtime Laravel Deployments
&lt;/h2&gt;

&lt;p&gt;Standard deployments cause a brief period of downtime while files are being uploaded and commands are running. For high-traffic applications, this is unacceptable. Zero-downtime deployment eliminates this entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Atomic Releases Work
&lt;/h3&gt;

&lt;p&gt;Instead of overwriting files in place, atomic releases use a directory structure with a symlink swap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/var/www/myapp/
├── releases/
│ ├── 20260301120000/ ← previous release
│ ├── 20260302150000/ ← current release (live)
│ └── 20260303090000/ ← new release (being prepared)
├── current → releases/20260302150000/ ← symlink
├── shared/
│ ├── storage/ ← persists across releases
│ └── .env ← persists across releases

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The deployment process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A[Clone new release] --&amp;gt; B[Install dependencies]
    B --&amp;gt; C[Build assets]
    C --&amp;gt; D[Run migrations]
    D --&amp;gt; E[Swap symlink]
    E --&amp;gt; F[Live traffic served]
    style E fill:#1abc9c,color:#fff

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;A new release directory is created&lt;/li&gt;
&lt;li&gt;Code is deployed into this directory&lt;/li&gt;
&lt;li&gt;Dependencies are installed and assets are built&lt;/li&gt;
&lt;li&gt;Shared directories (storage, .env) are symlinked into the new release&lt;/li&gt;
&lt;li&gt;Database migrations run against the new release&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;current&lt;/code&gt; symlink is atomically swapped to point to the new release&lt;/li&gt;
&lt;li&gt;If anything fails before the swap, &lt;code&gt;current&lt;/code&gt; still points to the previous working release&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The symlink swap is an atomic filesystem operation — there is no moment where the application is in a broken state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shared Files and Directories
&lt;/h3&gt;

&lt;p&gt;Some files and directories must persist across releases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.env&lt;/code&gt;&lt;/strong&gt; — environment configuration (database credentials, API keys)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;storage/&lt;/code&gt;&lt;/strong&gt; — logs, uploaded files, cache, sessions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;bootstrap/cache/&lt;/code&gt;&lt;/strong&gt; — sometimes shared depending on your setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles this automatically when you enable &lt;a href="https://www.deployhq.com/features/zero-downtime-deployments" rel="noopener noreferrer"&gt;zero-downtime deployments&lt;/a&gt; — you configure which paths are shared, and the symlinks are managed for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Instant Rollback
&lt;/h3&gt;

&lt;p&gt;With atomic releases, rolling back means changing the &lt;code&gt;current&lt;/code&gt; symlink to point at the previous release directory. This takes milliseconds. No files are modified, no git reverts, no partial states.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;, rollback is a single click in the deployment history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Laravel Deployment Checklist
&lt;/h2&gt;

&lt;p&gt;Use this checklist before every production deployment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[] &lt;code&gt;APP_ENV=production&lt;/code&gt; and &lt;code&gt;APP_DEBUG=false&lt;/code&gt; are set&lt;/li&gt;
&lt;li&gt;[] &lt;code&gt;APP_KEY&lt;/code&gt; is generated and set (never commit this to Git)&lt;/li&gt;
&lt;li&gt;[] &lt;code&gt;composer install --no-dev --optimize-autoloader&lt;/code&gt; runs without errors&lt;/li&gt;
&lt;li&gt;[] Frontend assets compile with &lt;code&gt;npm run build&lt;/code&gt; — check &lt;code&gt;public/build/&lt;/code&gt; exists&lt;/li&gt;
&lt;li&gt;[] Database migrations tested in staging first&lt;/li&gt;
&lt;li&gt;[] &lt;code&gt;php artisan optimize&lt;/code&gt; runs after deployment (caches config, routes, views, events)&lt;/li&gt;
&lt;li&gt;[] Queue workers restart after deployment (&lt;code&gt;php artisan queue:restart&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;[] &lt;code&gt;storage/&lt;/code&gt; and &lt;code&gt;bootstrap/cache/&lt;/code&gt; have correct write permissions&lt;/li&gt;
&lt;li&gt;[] Nginx or Apache serves from the &lt;code&gt;public/&lt;/code&gt; directory only&lt;/li&gt;
&lt;li&gt;[] &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;.git/&lt;/code&gt;, and &lt;code&gt;storage/&lt;/code&gt; are not web-accessible&lt;/li&gt;
&lt;li&gt;[] Health check endpoint (&lt;code&gt;/up&lt;/code&gt;) responds correctly&lt;/li&gt;
&lt;li&gt;[] Deployment notifications configured (Slack, Discord, or email)&lt;/li&gt;
&lt;li&gt;[] Zero-downtime deployments enabled for production&lt;/li&gt;
&lt;li&gt;[] At least 3-4 previous releases kept for rollback&lt;/li&gt;
&lt;li&gt;[] Queue workers monitored with Supervisor or &lt;a href="https://laravel.com/docs/12.x/horizon" rel="noopener noreferrer"&gt;Laravel Horizon&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting Common Deployment Issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;q&gt;500 Server Error&lt;/q&gt; After Deployment
&lt;/h3&gt;

&lt;p&gt;The most common cause: missing &lt;code&gt;.env&lt;/code&gt; file or &lt;code&gt;APP_KEY&lt;/code&gt;. Laravel cannot boot without an application key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check first&lt;/strong&gt; :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does &lt;code&gt;.env&lt;/code&gt; exist in the deployment directory?&lt;/li&gt;
&lt;li&gt;Is &lt;code&gt;APP_KEY&lt;/code&gt; set? Run &lt;code&gt;php artisan key:generate --show&lt;/code&gt; to verify.&lt;/li&gt;
&lt;li&gt;Clear cached config: &lt;code&gt;php artisan config:clear &amp;amp;&amp;amp; php artisan cache:clear&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check &lt;code&gt;storage/logs/laravel.log&lt;/code&gt; for the actual error message.&lt;/li&gt;
&lt;li&gt;Verify &lt;code&gt;storage/&lt;/code&gt; has write permissions: &lt;code&gt;chmod -R 775 storage bootstrap/cache&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Assets Not Loading (CSS/JS 404 Errors)
&lt;/h3&gt;

&lt;p&gt;Frontend build did not run or the Vite manifest is missing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check&lt;/strong&gt; :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does &lt;code&gt;public/build/manifest.json&lt;/code&gt; exist? If not, &lt;code&gt;npm run build&lt;/code&gt; did not complete.&lt;/li&gt;
&lt;li&gt;If using &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;, verify the build command (&lt;code&gt;npm ci &amp;amp;&amp;amp; npm run build&lt;/code&gt;) is in your build pipeline.&lt;/li&gt;
&lt;li&gt;Check that &lt;code&gt;APP_URL&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt; matches your actual domain — Vite uses this to generate asset URLs.&lt;/li&gt;
&lt;li&gt;If assets load on HTTP but not HTTPS, set &lt;code&gt;ASSET_URL=https://yourdomain.com&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Database Migration Failures
&lt;/h3&gt;

&lt;p&gt;Migrations failing in production while working locally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check&lt;/strong&gt; :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Did you include &lt;code&gt;--force&lt;/code&gt;? Laravel refuses to migrate in production without it.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;php artisan migrate --pretend&lt;/code&gt; to preview SQL without executing.&lt;/li&gt;
&lt;li&gt;Never run destructive migrations (dropping columns or tables) without a rollback plan.&lt;/li&gt;
&lt;li&gt;For large tables, consider running schema changes during low-traffic periods.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Queue Workers Serving Old Code
&lt;/h3&gt;

&lt;p&gt;After deployment, queue workers continue processing jobs with the old application code because they cache the application state in memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt; : Add &lt;code&gt;php artisan queue:restart&lt;/code&gt; to your post-deployment commands. This signals workers to finish their current job, then restart with the new code.&lt;/p&gt;

&lt;p&gt;If using &lt;a href="https://laravel.com/docs/12.x/horizon" rel="noopener noreferrer"&gt;Laravel Horizon&lt;/a&gt;, restart Horizon instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan horizon:terminate

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Supervisor (or your process manager) will automatically restart it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storage Symlink Not Working After Zero-Downtime Deploy
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;public/storage&lt;/code&gt; symlink points to &lt;code&gt;storage/app/public&lt;/code&gt;, but after a zero-downtime deployment, the path has changed because the release directory is new.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt; : Configure &lt;code&gt;storage&lt;/code&gt; as a shared directory in your zero-downtime settings. The symlink should point to the shared &lt;code&gt;storage/&lt;/code&gt; path, not the release-specific one. Then run &lt;code&gt;php artisan storage:link&lt;/code&gt; in your post-deployment commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you already have a server, the fastest path to automated Laravel deployment is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;Sign up for DeployHQ&lt;/a&gt; (free trial, no credit card)&lt;/li&gt;
&lt;li&gt;Connect your Git repository&lt;/li&gt;
&lt;li&gt;Add your server (SSH or SFTP)&lt;/li&gt;
&lt;li&gt;Set up build commands: &lt;code&gt;composer install --no-dev --optimize-autoloader &amp;amp;&amp;amp; npm ci &amp;amp;&amp;amp; npm run build&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add post-deploy SSH commands: &lt;code&gt;php artisan migrate --force &amp;amp;&amp;amp; php artisan optimize &amp;amp;&amp;amp; php artisan queue:restart&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enable auto-deploy on push to your main branch&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a detailed walkthrough with screenshots, see &lt;a href="https://www.deployhq.com/blog/deploying-a-laravel-react-application-to-fortrabbit-using-deployhq" rel="noopener noreferrer"&gt;How to&lt;/a&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; Laravel with DeployHQ. For help choosing between Forge and &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;, see &lt;a href="https://dev.to/deployhq/deployhq-vs-laravel-forge-how-they-differ-and-how-to-use-them-together-5e5n-temp-slug-4440760"&gt;DeployHQ vs Laravel Forge&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Questions about deploying Laravel? Reach out at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>infrastructure</category>
      <category>php</category>
      <category>laravel</category>
    </item>
    <item>
      <title>How to Deploy and Configure OpenClaw on a VPS</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Fri, 06 Mar 2026 18:21:56 +0000</pubDate>
      <link>https://dev.to/deployhq/how-to-deploy-and-configure-openclaw-on-a-vps-4377</link>
      <guid>https://dev.to/deployhq/how-to-deploy-and-configure-openclaw-on-a-vps-4377</guid>
      <description>&lt;p&gt;OpenClaw is one of the fastest-growing open-source projects of 2026 — 247,000 GitHub stars and counting. It acts as a self-hosted gateway between the messaging apps you already use (WhatsApp, Telegram, Discord, Slack) and AI models like Claude or GPT-4. Instead of paying for yet another SaaS AI assistant, you run it on your own VPS, keep your data private, and pay only for the API calls you actually make.&lt;/p&gt;

&lt;p&gt;This guide walks you through setting up OpenClaw on a Ubuntu VPS from scratch: provisioning, security hardening, Nginx with SSL, systemd service management, and automating configuration deployments with &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A VPS running &lt;strong&gt;Ubuntu 22.04 LTS&lt;/strong&gt; (minimum 2 vCPU, 4 GB RAM, 20 GB SSD — DigitalOcean, Contabo, or Hetzner all work)&lt;/li&gt;
&lt;li&gt;A domain name pointed at your VPS IP&lt;/li&gt;
&lt;li&gt;An API key from an AI provider (Anthropic, OpenAI, or Google Gemini)&lt;/li&gt;
&lt;li&gt;An account on your preferred messaging platform (Telegram is the easiest to start with)&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://deployhq.com/signup" rel="noopener noreferrer"&gt;DeployHQ account&lt;/a&gt; for automating updates (free tier covers this)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1 — Initial Server Setup
&lt;/h2&gt;

&lt;p&gt;Connect as root, then immediately create a non-root user with sudo access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh root@&amp;lt;your-vps-ip&amp;gt;

adduser openclawops
usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;openclawops

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy your SSH key to the new user so you can log back in without a password:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;--archive&lt;/span&gt; &lt;span class="nt"&gt;--chown&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;openclawops:openclawops ~/.ssh /home/openclawops

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switch to the new user for all remaining steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;su - openclawops

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tighten SSH configuration to disable password auth and root login:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/ssh/sshd_config

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set these values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;PasswordAuthentication&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
&lt;span class="n"&gt;PermitRootLogin&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart SSH (keep your current session open in case something goes wrong):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart ssh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the system before doing anything else:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt dist-upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2 — Configure the Firewall
&lt;/h2&gt;

&lt;p&gt;Lock down ingress traffic with UFW. Allow only SSH, HTTP, and HTTPS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw default deny incoming
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw default allow outgoing
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 22/tcp
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw limit 22/tcp &lt;span class="c"&gt;# rate-limit to slow brute-force attempts&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 80/tcp
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 443/tcp
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw &lt;span class="nb"&gt;enable&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the rules are active:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status verbose

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do &lt;strong&gt;not&lt;/strong&gt; open port 18789 (OpenClaw's gateway port) to the public internet. We'll put Nginx in front of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Install Node.js 22
&lt;/h2&gt;

&lt;p&gt;OpenClaw requires Node.js ≥22. Install it from the NodeSource repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://deb.nodesource.com/setup_22.x | &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; bash -
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nodejs
node &lt;span class="nt"&gt;--version&lt;/span&gt; &lt;span class="c"&gt;# should print v22.x.x&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4 — Install OpenClaw
&lt;/h2&gt;

&lt;p&gt;Run the official installer. It will walk you through an interactive setup wizard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://openclaw.ai/install.sh | bash

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During the wizard:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Accept the risk acknowledgment&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;QuickStart&lt;/strong&gt; mode&lt;/li&gt;
&lt;li&gt;Enter your AI provider API key (Anthropic, OpenAI, or Gemini)&lt;/li&gt;
&lt;li&gt;Select your messaging channel — &lt;strong&gt;Telegram&lt;/strong&gt; is recommended for VPS setups since it doesn't require port forwarding&lt;/li&gt;
&lt;li&gt;Enable bash completion&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once installed, verify the gateway started correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw gateway status

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the gateway bound to &lt;code&gt;127.0.0.1:18789&lt;/code&gt;. If it shows &lt;code&gt;0.0.0.0:18789&lt;/code&gt;, fix that immediately — the gateway would be publicly accessible without authentication.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 — Bind the Gateway to Localhost
&lt;/h2&gt;

&lt;p&gt;If the gateway is listening on all interfaces, rebind it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw configure

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When prompted for the gateway binding, select &lt;strong&gt;Local (this machine)&lt;/strong&gt;. This ensures the gateway only accepts connections from &lt;code&gt;127.0.0.1&lt;/code&gt;, and all external access goes through Nginx.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6 — Set Up Nginx as a Reverse Proxy
&lt;/h2&gt;

&lt;p&gt;Install Nginx and Certbot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx certbot python3-certbot-nginx

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new server block for OpenClaw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/sites-available/openclaw

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the following, replacing &lt;code&gt;openclaw.example.com&lt;/code&gt; with your actual domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="s"&gt;openclaw.example.com&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:18789&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_http_version&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Upgrade&lt;/span&gt; &lt;span class="nv"&gt;$http_upgrade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Connection&lt;/span&gt; &lt;span class="s"&gt;"upgrade"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;Host&lt;/span&gt; &lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Real-IP&lt;/span&gt; &lt;span class="nv"&gt;$remote_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-For&lt;/span&gt; &lt;span class="nv"&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_set_header&lt;/span&gt; &lt;span class="s"&gt;X-Forwarded-Proto&lt;/span&gt; &lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;proxy_read_timeout&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;proxy_read_timeout 86400&lt;/code&gt; (24 hours) prevents Nginx from closing long-running WebSocket connections that OpenClaw uses for real-time messaging.&lt;/p&gt;

&lt;p&gt;Enable the site and test the config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/openclaw /etc/nginx/sites-enabled/
&lt;span class="nb"&gt;sudo &lt;/span&gt;nginx &lt;span class="nt"&gt;-t&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload nginx

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obtain a free TLS certificate from Let's Encrypt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; openclaw.example.com

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Certbot automatically reconfigures Nginx for HTTPS and sets up auto-renewal. Your gateway is now accessible at &lt;code&gt;https://openclaw.example.com&lt;/code&gt; with a valid certificate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7 — Run OpenClaw as a Systemd Service
&lt;/h2&gt;

&lt;p&gt;Right now OpenClaw stops when you close your SSH session. Fix that by creating a systemd unit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="err"&gt;sudo&lt;/span&gt; &lt;span class="err"&gt;nano&lt;/span&gt; &lt;span class="err"&gt;/etc/systemd/system/openclaw.service&lt;/span&gt;


&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;OpenClaw Gateway&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;openclawops&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/openclawops&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/bin/openclaw gateway start&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-failure&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;
&lt;span class="py"&gt;StandardOutput&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;journal&lt;/span&gt;
&lt;span class="py"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;journal&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable and start the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;openclaw
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start openclaw
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status openclaw

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OpenClaw now starts automatically on boot and restarts if it crashes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8 — Connect Your Messaging Channel
&lt;/h2&gt;

&lt;p&gt;With the gateway running, add a Telegram bot:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open Telegram and message &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/botfather"&gt;@botfather&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;/newbot&lt;/code&gt; and follow the prompts — you'll receive a bot token&lt;/li&gt;
&lt;li&gt;On your server, run:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openclaw channels add telegram

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter the bot token when prompted. Send your bot a message on Telegram — OpenClaw should respond immediately via whichever AI provider you configured.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9 — Automate Configuration Updates with DeployHQ
&lt;/h2&gt;

&lt;p&gt;As you customise OpenClaw — adding &lt;a href="https://deployhq.com/blog/openclaw-skills-guide" rel="noopener noreferrer"&gt;Skills&lt;/a&gt; (plugins for Gmail, GitHub, Notion, Home Assistant, and 100+ others), tweaking system prompts, or updating webhook configs — you'll want those changes deployed to your VPS automatically rather than manually SSH-ing in.&lt;/p&gt;

&lt;p&gt;The pattern is straightforward: store your OpenClaw configuration and custom skills in a Git repository, then use &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; to deploy changes to your VPS whenever you push.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set up your config repository:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/openclaw-config &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/openclaw-config
git init

&lt;span class="c"&gt;# Copy any custom skills or config overrides into this directory&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; ~/.openclaw/config.json ./config.json
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; ~/.openclaw/skills ./skills

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create a deployment script&lt;/strong&gt; (&lt;code&gt;deploy.sh&lt;/code&gt;) that your VPS runs on every push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="c"&gt;# Copy config updates into place&lt;/span&gt;
&lt;span class="nb"&gt;cp &lt;/span&gt;config.json ~/.openclaw/config.json
rsync &lt;span class="nt"&gt;-av&lt;/span&gt; skills/ ~/.openclaw/skills/

&lt;span class="c"&gt;# Restart the gateway to pick up changes&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart openclaw

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"OpenClaw updated and restarted"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Push this to your Git host (GitHub, GitLab, Bitbucket, or &lt;a href="https://codebaseapp.com" rel="noopener noreferrer"&gt;Codebase&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial OpenClaw config"&lt;/span&gt;
git remote add origin &amp;lt;your-repo-url&amp;gt;
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Connect DeployHQ:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://deployhq.com" rel="noopener noreferrer"&gt;Log in to DeployHQ&lt;/a&gt; and create a new project&lt;/li&gt;
&lt;li&gt;Connect it to your repository&lt;/li&gt;
&lt;li&gt;Add your VPS as a server (SSH key authentication)&lt;/li&gt;
&lt;li&gt;Set the deployment path to &lt;code&gt;/home/openclawops/openclaw-config&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add a post-deployment command: &lt;code&gt;bash deploy.sh&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now every time you push a config change or add a new skill, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; runs the deployment automatically. No more SSH sessions for routine updates — and you get a full deployment history with rollback if something breaks.&lt;/p&gt;

&lt;p&gt;This same workflow applies to any Node.js application you're running on your VPS. &lt;a href="https://deployhq.com/support/deploy-node-js" rel="noopener noreferrer"&gt;DeployHQ supports deploying Node.js apps&lt;/a&gt; alongside OpenClaw, so you can manage multiple services on the same server from a single dashboard. You can also use &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; to deploy Python-based tools — for instance, &lt;a href="https://deployhq.com/blog/scrape-applications-using-scraperapi-and-deployhq" rel="noopener noreferrer"&gt;scraping applications with ScraperAPI and DeployHQ&lt;/a&gt; for automated data collection alongside your AI assistant.&lt;/p&gt;




&lt;h2&gt;
  
  
  Keeping OpenClaw Updated
&lt;/h2&gt;

&lt;p&gt;OpenClaw releases frequently. To upgrade the CLI itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm update &lt;span class="nt"&gt;-g&lt;/span&gt; openclaw
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart openclaw

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the &lt;a href="https://github.com/openclaw/openclaw" rel="noopener noreferrer"&gt;OpenClaw GitHub releases&lt;/a&gt; for breaking changes before upgrading on production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Gateway won't start&lt;/strong&gt; : Check logs with &lt;code&gt;journalctl -u openclaw -n 50&lt;/code&gt;. Most failures are missing environment variables (API key not set) or port conflicts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nginx 502 Bad Gateway&lt;/strong&gt; : The OpenClaw gateway isn't running. Run &lt;code&gt;openclaw gateway status&lt;/code&gt; and check systemd logs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Telegram bot not responding&lt;/strong&gt; : Verify the bot token is correct with &lt;code&gt;openclaw channels list&lt;/code&gt;. Also check that your VPS can reach &lt;code&gt;api.telegram.org&lt;/code&gt; — some providers block outbound traffic by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Certificate renewal failing&lt;/strong&gt; : Run &lt;code&gt;sudo certbot renew --dry-run&lt;/code&gt; to debug. Certbot needs ports 80 and 443 open and Nginx running.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Do I need a domain name?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No — you can skip Steps 6 entirely and access the Control UI via an SSH tunnel instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 18789:127.0.0.1:18789 openclawops@&amp;lt;your-vps-ip&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open &lt;code&gt;http://localhost:18789&lt;/code&gt; in your browser. The downside is you need the tunnel running whenever you want to use the web UI. If you're using Telegram or another messaging channel, those work fine without any web access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which AI provider should I use?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Anthropic's Claude is the most capable for complex tasks and follows instructions precisely. OpenAI's GPT-4o is a solid all-rounder and has slightly broader third-party skill compatibility. Google Gemini Flash is the cheapest option for high-volume use. You can configure multiple providers and switch between them per conversation — useful if you want cheaper models for quick queries and stronger ones for coding tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can multiple people share one OpenClaw instance?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. OpenClaw supports multiple channels and workspace isolation, so you can connect separate Telegram bots or Slack workspaces and route them to different agent configurations. Each workspace gets its own memory, system prompt, and tool access. This is useful for teams where each member wants their own assistant context without running separate VPS instances.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much will API costs actually run?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For typical personal use — a few dozen queries per day — expect $5–$15/month in API costs on top of your VPS bill. Heavy use with Claude Opus or GPT-4 can push that higher. Switching to Claude Haiku or Gemini Flash for routine tasks and reserving the stronger models for complex ones is the most effective way to keep costs in check. OpenClaw logs every API call with token counts, so you can monitor usage from the Control UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is my data private if I use a cloud AI provider?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your messages are sent to whichever AI provider's API you configure. OpenClaw itself keeps all session history and memory on your VPS — nothing is stored by third parties beyond what the AI API processes to generate a response. If you need full end-to-end privacy, you can run a local model (via Ollama) and point OpenClaw at it — your data then never leaves the VPS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I run other services on the same VPS?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. OpenClaw runs as a single Node.js process and is relatively lightweight — a 4 GB RAM server comfortably runs OpenClaw alongside a small web app, database, or other services. Use separate Nginx server blocks for each service and manage them all through &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I back up my OpenClaw configuration?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The configuration the &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; workflow covers is the primary backup strategy — your &lt;code&gt;config.json&lt;/code&gt; and custom skills live in Git, which is your source of truth. For session memory and conversation history stored by OpenClaw locally, back up the &lt;code&gt;~/.openclaw/data&lt;/code&gt; directory periodically. A simple cron job to &lt;code&gt;rsync&lt;/code&gt; it to an S3-compatible bucket is sufficient for most setups.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when OpenClaw releases a breaking update?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check the &lt;a href="https://github.com/openclaw/openclaw" rel="noopener noreferrer"&gt;CHANGELOG&lt;/a&gt; before running &lt;code&gt;npm update -g openclaw&lt;/code&gt;. Breaking changes are rare but do happen — usually around skill API changes. Test on a staging VPS first if you have critical automations running, then update production. The &lt;code&gt;Restart=on-failure&lt;/code&gt; in your systemd unit means even a bad update that crashes the process won't leave you without a running service — it'll keep retrying until you fix the config.&lt;/p&gt;




&lt;p&gt;OpenClaw gives you a genuinely useful AI assistant that runs on infrastructure you control — no subscription lock-in, no data leaving your servers unless you choose it. A $5-$10/month VPS handles typical personal use comfortably, and your only ongoing cost is API usage.&lt;/p&gt;

&lt;p&gt;For teams managing multiple VPS instances, &lt;a href="https://deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; simplifies keeping configurations consistent across servers — deploy once to all your nodes from a single push. &lt;a href="https://deployhq.com/signup" rel="noopener noreferrer"&gt;Sign up free&lt;/a&gt; and connect your first server in minutes.&lt;/p&gt;

&lt;p&gt;Have questions or hit an issue? Reach out at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or find us on Twitter at &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;@deployhq&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>tutorials</category>
      <category>vps</category>
      <category>openclaw</category>
    </item>
    <item>
      <title>Game Panel Integration: Deploy to Your Game Server from Git</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Fri, 06 Mar 2026 08:09:47 +0000</pubDate>
      <link>https://dev.to/deployhq/game-panel-integration-deploy-to-your-game-server-from-git-144a</link>
      <guid>https://dev.to/deployhq/game-panel-integration-deploy-to-your-game-server-from-git-144a</guid>
      <description>&lt;p&gt;If you run a Minecraft, Rust, Garry's Mod, or FiveM server, you're probably familiar with the routine: download a plugin update, open FileZilla, connect via SFTP, navigate to the right folder, upload, restart, and hope nothing breaks.&lt;/p&gt;

&lt;p&gt;Now multiply that across multiple servers, multiple admins, and dozens of plugins. One wrong config file overwrites another, there's no record of who changed what, and rolling back means restoring from a backup — if you even have one.&lt;/p&gt;

&lt;p&gt;We're introducing &lt;strong&gt;Game Panel integration&lt;/strong&gt; to &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;, starting with native support for the &lt;strong&gt;Pterodactyl protocol&lt;/strong&gt;. This means you can deploy plugins, mods, configs, and scripts to your game server directly from Git — with automatic deployments on push, full deployment history, and one-click rollback.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why deploy your game server from Git?
&lt;/h2&gt;

&lt;p&gt;If you're managing a community Minecraft server with 30+ plugins, a DarkRP Garry's Mod server with custom Lua addons, or a Rust server with uMod configurations, you're managing a codebase — whether you think of it that way or not.&lt;/p&gt;

&lt;p&gt;Version control with Git gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full history of every change.&lt;/strong&gt; Know exactly when a plugin was updated, which config was modified, and who did it. No more &lt;q&gt;who changed server.properties?&lt;/q&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instant rollback.&lt;/strong&gt; A plugin update breaks your server? Revert to the last working deployment in &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; with one click. No digging through backup archives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Branches for testing.&lt;/strong&gt; Set up a test server alongside your production server. Make changes in a branch, test them, then merge and deploy to production when you're confident.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team coordination.&lt;/strong&gt; Multiple admins can work on configs in their own branches and merge via pull requests. No more stepping on each other's changes over SFTP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how professional development teams have worked for decades. Game server admins deserve the same workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The setup takes about five minutes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Put your server files in a Git repository.&lt;/strong&gt; Your plugins, mods, configs, and custom scripts go in a Git repo on GitHub, GitLab, or Bitbucket. World files, player data, and logs stay out (add them to &lt;code&gt;.gitignore&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; project.&lt;/strong&gt; Connect your repository and add your game server — either using SFTP credentials from your hosting panel, or with the new Pterodactyl protocol for hosts that run Pterodactyl.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Push to deploy.&lt;/strong&gt; Every time you push to your repository, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; uploads only the files that changed to your game server. No full re-uploads, no missed files.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. No YAML files to write, no CI/CD pipelines to configure, no SFTP credentials saved in browser bookmarks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pterodactyl protocol support (beta)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://pterodactyl.io/" rel="noopener noreferrer"&gt;Pterodactyl&lt;/a&gt; is the open-source game server management panel used by dozens of hosting providers. If your host's panel lets you manage your server through a web interface with a file manager, console, and power controls — there's a good chance it runs on Pterodactyl (or one of its forks like Pelican or Pyrodactyl).&lt;/p&gt;

&lt;p&gt;Hosts running Pterodactyl include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;BisectHosting (branded as &lt;q&gt;Starbase Panel&lt;/q&gt;)&lt;/li&gt;
&lt;li&gt;PebbleHost&lt;/li&gt;
&lt;li&gt;Bloom.host&lt;/li&gt;
&lt;li&gt;GGServers&lt;/li&gt;
&lt;li&gt;Sparked Host&lt;/li&gt;
&lt;li&gt;And many more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Until now, connecting &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; to a Pterodactyl-based server meant configuring SFTP manually — finding the right hostname, using port 2022 instead of the standard 22, and figuring out the username format (&lt;code&gt;username.serverid&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;With the new Pterodactyl protocol, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; connects natively. Select &lt;q&gt;Game Panel&lt;/q&gt; as your server type, enter your panel URL and API credentials, and &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can you deploy?
&lt;/h2&gt;

&lt;p&gt;Anything that lives on your game server's filesystem:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File type&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;th&gt;Games&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Plugins&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;.jar files (Spigot, Paper, Bukkit), .cs files (uMod, Carbon), .lua scripts&lt;/td&gt;
&lt;td&gt;Minecraft, Rust, GMod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mods&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Forge/Fabric mods, BepInEx plugins, modpack configs&lt;/td&gt;
&lt;td&gt;Minecraft, Valheim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Configurations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;server.properties, YAML/JSON configs, game settings&lt;/td&gt;
&lt;td&gt;All games&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Custom scripts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lua addons, DarkRP configs, FiveM resources&lt;/td&gt;
&lt;td&gt;GMod, FiveM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Maps and worlds&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Custom maps, level files&lt;/td&gt;
&lt;td&gt;Minecraft, ARK, CS2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use &lt;a href="https://www.deployhq.com/support/excluded-files" rel="noopener noreferrer"&gt;Excluded files&lt;/a&gt; to keep server-generated content (world saves, player data, logs) out of your deployments. Use &lt;a href="https://www.deployhq.com/support/config-files" rel="noopener noreferrer"&gt;Config files&lt;/a&gt; to manage server-specific settings that shouldn't live in your repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  The alternative: 50 lines of YAML
&lt;/h2&gt;

&lt;p&gt;The only other way to automate game server deployments today is to build a CI/CD pipeline yourself using GitHub Actions. GGServers published &lt;a href="https://ggservers.com/knowledgebase/article/how-to-automatically-deploy-to-your-pterodactyl-server-using-github-actions-full-ci-cd-guide/" rel="noopener noreferrer"&gt;a guide for this&lt;/a&gt; — it involves writing a YAML workflow file, storing SFTP credentials as GitHub Secrets, configuring LFTP mirror commands, and managing file exclusion patterns manually.&lt;/p&gt;

&lt;p&gt;It works, but it requires DevOps knowledge that most game server admins don't have (and shouldn't need). There's no deployment history UI, no rollback button, no way to see what was deployed and when without reading CI logs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; replaces all of that with a configured server connection and a deploy button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Works with every game server host
&lt;/h2&gt;

&lt;p&gt;Game Panel integration with Pterodactyl is just the beginning. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; already connects to any game server with FTP or SFTP access — which is virtually all of them.&lt;/p&gt;

&lt;p&gt;We have step-by-step guides for connecting to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com/guides/apex-hosting" rel="noopener noreferrer"&gt;Apex Hosting&lt;/a&gt; (FTP, port 21)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com/guides/bisecthosting" rel="noopener noreferrer"&gt;BisectHosting&lt;/a&gt; (SFTP, port 2022)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com/guides/gportal" rel="noopener noreferrer"&gt;GPortal&lt;/a&gt; (FTP/SFTP)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com/guides/nitrado" rel="noopener noreferrer"&gt;Nitrado&lt;/a&gt; (FTP/SFTP, port 21)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com/guides/nodecraft" rel="noopener noreferrer"&gt;Nodecraft&lt;/a&gt; (FTPS, port 121)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com/guides/pebblehost" rel="noopener noreferrer"&gt;PebbleHost&lt;/a&gt; (SFTP, port 2022)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com/guides/physgun" rel="noopener noreferrer"&gt;Physgun&lt;/a&gt; (SFTP)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com/guides/shockbyte" rel="noopener noreferrer"&gt;Shockbyte&lt;/a&gt; (SFTP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Running your own server on a VPS? Connect via SSH/SFTP as usual — we have guides for &lt;a href="https://www.deployhq.com/guides/digitalocean" rel="noopener noreferrer"&gt;DigitalOcean&lt;/a&gt;, &lt;a href="https://www.deployhq.com/guides/hetzner" rel="noopener noreferrer"&gt;Hetzner&lt;/a&gt;, &lt;a href="https://www.deployhq.com/guides/linode" rel="noopener noreferrer"&gt;Akamai (Linode)&lt;/a&gt;, and &lt;a href="https://www.deployhq.com/guides" rel="noopener noreferrer"&gt;many more&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;Sign up for a free&lt;/a&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; account&lt;/li&gt;
&lt;li&gt;Connect your Git repository (GitHub, GitLab, or Bitbucket)&lt;/li&gt;
&lt;li&gt;Add your game server — via Game Panel (Pterodactyl), SFTP, or FTP&lt;/li&gt;
&lt;li&gt;Push a commit and watch it deploy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No more FileZilla. No more manual uploads. No more &lt;q&gt;who broke the server?&lt;/q&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Do I need SSH access to my game server?&lt;/strong&gt; No. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; works with FTP and SFTP, which virtually every game server host provides. SSH is not required. If your host runs Pterodactyl, the upcoming Game Panel integration makes setup even easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; restart my server after deploying?&lt;/strong&gt;Not automatically via SFTP/FTP — you'll restart from your game panel after deployment. With the Pterodactyl Game Panel integration, we're exploring webhook-based restart support so your server can restart automatically after a successful deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I deploy to multiple servers at once?&lt;/strong&gt; Yes. Add multiple servers to your &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; project and deploy to all of them with a single push. This is useful for server networks or test/production setups.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if a deployment breaks my server?&lt;/strong&gt; Use DeployHQ's rollback feature to revert to the last working deployment in one click. Every deployment is recorded, so you can always go back to a known good state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should I put my entire server in Git?&lt;/strong&gt; No — only put files you actively manage in your repository. Plugins, mods, configs, and custom scripts belong in Git. World saves, player data, logs, and server-generated files should be excluded using a &lt;code&gt;.gitignore&lt;/code&gt; file and DeployHQ's &lt;a href="https://www.deployhq.com/support/excluded-files" rel="noopener noreferrer"&gt;Excluded files&lt;/a&gt; feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does this work with modpacks?&lt;/strong&gt; Yes. Store your modpack configuration (mod JARs, config files, resource packs) in a Git repository and deploy as normal. For large modpacks with hundreds of files, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; only uploads files that have changed — so updates are fast even on large servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; free?&lt;/strong&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; offers a free plan for personal projects with 1 project and 3 deployments per day. Paid plans start at €9/month with unlimited deployments and a free 10-day trial — no credit card required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I use Multicraft, not Pterodactyl. Can I still use &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;?&lt;/strong&gt;Yes. The Game Panel integration is specifically for Pterodactyl-based hosts, but &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; connects to any server via standard FTP or SFTP — regardless of which control panel your host uses.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have questions about deploying to your game server? &lt;a href="https://www.deployhq.com/contact" rel="noopener noreferrer"&gt;Contact our support team&lt;/a&gt; or reach out on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>launches</category>
      <category>newfeatures</category>
      <category>gamepanel</category>
      <category>pterodactyl</category>
    </item>
    <item>
      <title>Deployment Agents Compared: DeployHQ vs Buddy vs Octopus Deploy</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Wed, 04 Mar 2026 13:29:00 +0000</pubDate>
      <link>https://dev.to/deployhq/deployment-agents-compared-deployhq-vs-buddy-vs-octopus-deploy-el1</link>
      <guid>https://dev.to/deployhq/deployment-agents-compared-deployhq-vs-buddy-vs-octopus-deploy-el1</guid>
      <description>&lt;p&gt;When your servers sit behind a firewall, most deployment tools cannot reach them. The standard fix is a deployment agent — a lightweight process that runs inside your private network, connects outbound to the deployment platform, and routes deployment traffic through that tunnel.&lt;/p&gt;

&lt;p&gt;Several tools offer this pattern, but the implementations differ significantly. This guide compares three deployment agents side by side: the &lt;a href="https://www.deployhq.com/features/deploy-behind-firewalls" rel="noopener noreferrer"&gt;DeployHQ Agent&lt;/a&gt;, the &lt;a href="https://buddy.works/docs/tunnels-agents/proxy-agent" rel="noopener noreferrer"&gt;Buddy Proxy Agent&lt;/a&gt;, and the &lt;a href="https://octopus.com/docs/infrastructure/deployment-targets/tentacle" rel="noopener noreferrer"&gt;Octopus&lt;/a&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; Polling Tentacle — covering architecture, setup, protocols, security, pricing, and the scenarios where each one fits best.&lt;/p&gt;

&lt;p&gt;If you are not sure whether you need a deployment agent in the first place, read our guide on &lt;a href="https://www.deployhq.com/blog/deploying-behind-firewalls-made-easy-introducing-the-deployhq-agent" rel="noopener noreferrer"&gt;how to deploy to servers behind a firewall&lt;/a&gt;, which compares agents against SSH tunnels, VPNs, bastion hosts, and self-hosted CI/CD runners.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Deployment Agents Work
&lt;/h2&gt;

&lt;p&gt;All three tools solve the same fundamental problem: your deployment platform lives in the cloud, your server lives behind a firewall, and the firewall blocks inbound connections.&lt;/p&gt;

&lt;p&gt;The solution is to flip the direction. Instead of the platform connecting inbound to your server, an agent on your server connects outbound to the platform. Since firewalls typically allow outbound traffic, the connection goes through. The platform then sends deployment instructions back through this established tunnel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Agent] --&amp;gt;|Outbound connection| B[Platform]
    B --&amp;gt;|Deploy traffic| A
    A --&amp;gt;|Local network| C[Server]
    style A fill:#1abc9c,color:#fff

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the three tools diverge is in the tunnel protocol, the agent-to-server relationship, and the scope of what the platform does beyond deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  DeployHQ Agent: One Tunnel, Any Protocol
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; Agent establishes a persistent outbound TLS connection to &lt;code&gt;agent.deployhq.com&lt;/code&gt; on TCP port 7777. Once the tunnel is up, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; routes deployment traffic through it using whatever protocol the deployment target requires — FTP, SFTP, SSH, or S3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[DeployHQAgent] --&amp;gt;|TLS port 7777| B[DeployHQ]
    B --&amp;gt;|FTP/SFTP/SSH/S3| A
    A --&amp;gt;|Any protocol| C[Server1]
    A --&amp;gt;|Any protocol| D[Server2]
    A --&amp;gt;|Any protocol| E[Server3]
    style A fill:#1abc9c,color:#fff

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent acts as a network proxy. One agent instance can deploy to multiple internal servers — you control which ones via an access file (&lt;code&gt;~/.deploy/agent.access&lt;/code&gt;) that accepts individual IPs, hostnames, or CIDR ranges like &lt;code&gt;192.168.1.0/24&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Key technical details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tunnel protocol&lt;/strong&gt; : TLS over TCP (port 7777 outbound)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment protocols&lt;/strong&gt; : FTP, SFTP, SSH, S3 — protocol-agnostic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent-to-server ratio&lt;/strong&gt; : One agent, many servers (proxy model)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime&lt;/strong&gt; : Ruby 2.2+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platforms&lt;/strong&gt; : Linux, macOS, Windows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source&lt;/strong&gt; : Yes — &lt;a href="https://github.com/deployhq/deploy-agent" rel="noopener noreferrer"&gt;deployhq/deploy-agent on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reconnection&lt;/strong&gt; : Automatic — runs as a background service&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Buddy Proxy Agent: SSH Tunnels Under the Hood
&lt;/h3&gt;

&lt;p&gt;The Buddy Proxy Agent establishes an outbound SSH connection to Buddy's infrastructure and keeps a reverse SSH tunnel alive. Buddy routes deployment traffic back through this tunnel to reach servers inside the private network.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[BuddyAgent] --&amp;gt;|SSH tunnel| B[Buddy]
    B --&amp;gt;|SSH/SFTP only| A
    A --&amp;gt;|SSH/SFTP| C[Server1]
    A --&amp;gt;|SSH/SFTP| D[Server2]
    style A fill:#9b59b6,color:#fff

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like DeployHQ's agent, Buddy's agent acts as a proxy — one agent can route traffic to multiple internal servers. You configure target servers as SFTP or SSH targets within Buddy, selecting the agent as the proxy during setup.&lt;/p&gt;

&lt;p&gt;Key technical details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tunnel protocol&lt;/strong&gt; : SSH (reverse tunnel)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment protocols&lt;/strong&gt; : SSH and SFTP only — no FTP or S3 support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent-to-server ratio&lt;/strong&gt; : One agent, many servers (proxy model)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime&lt;/strong&gt; : Docker-based agent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platforms&lt;/strong&gt; : Linux (Docker required)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source&lt;/strong&gt; : No&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reconnection&lt;/strong&gt; : Automatic while agent container is running&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Octopus Deploy Polling Tentacle: Agent Per Machine
&lt;/h3&gt;

&lt;p&gt;The Octopus Polling Tentacle takes a fundamentally different approach. Instead of one proxy for the network, you install a Tentacle on every deployment target. Each Tentacle independently polls the Octopus Server over HTTPS (port 443 or 10943) asking for work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Tentacle1] --&amp;gt;|HTTPS poll| B[OctopusServer]
    C[Tentacle2] --&amp;gt;|HTTPS poll| B
    D[Tentacle3] --&amp;gt;|HTTPS poll| B
    style A fill:#e67e22,color:#fff
    style C fill:#e67e22,color:#fff
    style D fill:#e67e22,color:#fff

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the Octopus Server has a deployment task for a target, it responds to the next poll with the deployment package and instructions. The Tentacle executes the deployment steps locally — no traffic proxying involved.&lt;/p&gt;

&lt;p&gt;Key technical details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tunnel protocol&lt;/strong&gt; : HTTPS polling (port 443 or 10943 outbound), WebSocket option available&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment protocols&lt;/strong&gt; : Custom Octopus protocol (Halibut) — packages are pushed to the Tentacle, which executes deployment steps locally&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent-to-server ratio&lt;/strong&gt; : One Tentacle per server (per-machine model)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime&lt;/strong&gt; : .NET&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platforms&lt;/strong&gt; : Windows, Linux, Docker containers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source&lt;/strong&gt; : Tentacle is open source; Octopus Server is not&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt; : Certificate-based mutual TLS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reconnection&lt;/strong&gt; : Automatic polling on configurable interval&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Side-by-Side Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DeployHQ Agent&lt;/th&gt;
&lt;th&gt;Buddy Proxy Agent&lt;/th&gt;
&lt;th&gt;Octopus Polling Tentacle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Proxy (one agent, many servers)&lt;/td&gt;
&lt;td&gt;Proxy (one agent, many servers)&lt;/td&gt;
&lt;td&gt;Per-machine (one Tentacle per server)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tunnel protocol&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TLS (TCP 7777)&lt;/td&gt;
&lt;td&gt;SSH (reverse tunnel)&lt;/td&gt;
&lt;td&gt;HTTPS polling (443/10943)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment protocols&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FTP, SFTP, SSH, S3&lt;/td&gt;
&lt;td&gt;SSH, SFTP only&lt;/td&gt;
&lt;td&gt;Custom (Halibut)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-server access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes — CIDR-based ACL&lt;/td&gt;
&lt;td&gt;Yes — configure per target&lt;/td&gt;
&lt;td&gt;N/A — one per machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agents to manage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1 per network&lt;/td&gt;
&lt;td&gt;1 per network&lt;/td&gt;
&lt;td&gt;1 per server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Runtime requirement&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ruby 2.2+&lt;/td&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;.NET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Platforms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Linux, macOS, Windows&lt;/td&gt;
&lt;td&gt;Linux (Docker)&lt;/td&gt;
&lt;td&gt;Windows, Linux, Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Open source&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Tentacle only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API token&lt;/td&gt;
&lt;td&gt;SSH key&lt;/td&gt;
&lt;td&gt;Mutual TLS certificates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inbound ports needed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WebSocket support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (Windows)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Setup time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~5 minutes&lt;/td&gt;
&lt;td&gt;~10 minutes&lt;/td&gt;
&lt;td&gt;~15 minutes per server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Proxy Model vs the Per-Machine Model
&lt;/h2&gt;

&lt;p&gt;This is the most important architectural difference and it affects everything from setup time to security posture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Proxy Model (DeployHQ, Buddy)
&lt;/h3&gt;

&lt;p&gt;One agent sits at the edge of your private network and proxies deployment traffic to internal servers. You install and manage one process. Adding a new server means updating a config file (DeployHQ) or adding a target in the UI (Buddy) — no software installed on the target server itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimal infrastructure overhead — one process to monitor and maintain&lt;/li&gt;
&lt;li&gt;Adding or removing servers does not require installing or uninstalling agents&lt;/li&gt;
&lt;li&gt;The agent machine is the only one that needs outbound internet access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single point of failure — if the agent goes down, all deployments to that network stop&lt;/li&gt;
&lt;li&gt;The agent machine has network access to every whitelisted server, which means compromising it could allow lateral movement&lt;/li&gt;
&lt;li&gt;Deployment protocols are limited to what the platform supports through the tunnel&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Per-Machine Model (Octopus)
&lt;/h3&gt;

&lt;p&gt;Every deployment target runs its own agent. Each one independently connects to the Octopus Server and executes deployments locally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No single point of failure — one Tentacle going down only affects that server&lt;/li&gt;
&lt;li&gt;Each Tentacle can have independent security policies, update schedules, and permissions&lt;/li&gt;
&lt;li&gt;Deployments execute locally on the target, so no file transfer protocol limitations&lt;/li&gt;
&lt;li&gt;Better fit for environments where different teams own different servers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More infrastructure to manage — N servers means N agents to install, monitor, patch, and update&lt;/li&gt;
&lt;li&gt;Every target server needs outbound internet access (or access to a shared Octopus Server)&lt;/li&gt;
&lt;li&gt;Adding a new deployment target means installing and configuring a new Tentacle&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Which Model Fits?
&lt;/h3&gt;

&lt;p&gt;The proxy model is simpler for small to medium deployments where one team manages a cluster of servers behind a single firewall. The per-machine model makes more sense in enterprise environments with dozens of servers, multiple teams, and compliance requirements that demand per-target audit trails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protocol Support
&lt;/h2&gt;

&lt;p&gt;This is where DeployHQ's agent stands out.&lt;/p&gt;

&lt;p&gt;Many legacy applications and hosting environments rely on FTP or SFTP for deployments — shared hosting with cPanel, older WordPress setups, and environments where SSH access is not available. Buddy's agent only supports SSH and SFTP, which means you cannot deploy to FTP-only servers through the proxy.&lt;/p&gt;

&lt;p&gt;DeployHQ's agent proxies at the network level, supporting FTP, SFTP, SSH, and S3 through the same tunnel. This makes it the only option for teams that need to deploy to a mix of modern and legacy infrastructure behind the same firewall.&lt;/p&gt;

&lt;p&gt;Octopus takes a different approach entirely — the Tentacle does not proxy protocols. Instead, Octopus sends deployment packages to the Tentacle, which executes deployment steps locally. This means protocol support is not a factor because files never transfer through a tunnel in the traditional sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Comparison
&lt;/h2&gt;

&lt;p&gt;All three tools avoid inbound firewall rules, but their security models differ:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DeployHQ Agent&lt;/strong&gt; : Outbound TLS on a single port (7777). The access control file explicitly whitelists which internal servers the agent can reach, limiting blast radius if the agent machine is compromised. The agent source code is fully auditable on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Buddy Proxy Agent&lt;/strong&gt; : Outbound SSH tunnel. SSH provides strong encryption, but the agent is not open source, so you cannot audit what runs inside the container. Access control is managed through Buddy's UI rather than a local config file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Octopus Polling Tentacle&lt;/strong&gt; : Outbound HTTPS with certificate-based mutual TLS authentication. This is the strongest authentication model of the three — both the server and the Tentacle verify each other's identity via certificates. However, each Tentacle has full local access to its host machine, and the .NET runtime has a larger attack surface than a Ruby gem or Docker container.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Security Factor&lt;/th&gt;
&lt;th&gt;DeployHQ&lt;/th&gt;
&lt;th&gt;Buddy&lt;/th&gt;
&lt;th&gt;Octopus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Encryption&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TLS&lt;/td&gt;
&lt;td&gt;SSH&lt;/td&gt;
&lt;td&gt;HTTPS/TLS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API token&lt;/td&gt;
&lt;td&gt;SSH key&lt;/td&gt;
&lt;td&gt;Mutual TLS certificates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Access control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local file (IP/CIDR)&lt;/td&gt;
&lt;td&gt;Platform UI&lt;/td&gt;
&lt;td&gt;Per-Tentacle certificates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Open source agent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Tentacle only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Attack surface&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ruby gem&lt;/td&gt;
&lt;td&gt;Docker container&lt;/td&gt;
&lt;td&gt;.NET runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Blast radius if compromised&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All whitelisted servers&lt;/td&gt;
&lt;td&gt;All configured targets&lt;/td&gt;
&lt;td&gt;Single server only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Deployment agents are typically bundled with the platform, but the platforms themselves have very different pricing models.&lt;/p&gt;

&lt;h3&gt;
  
  
  DeployHQ
&lt;/h3&gt;

&lt;p&gt;Pricing is based on projects and servers. The Agent is included on all plans, including the free tier.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Projects&lt;/th&gt;
&lt;th&gt;Servers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;€0/mo&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Solo&lt;/td&gt;
&lt;td&gt;€9/mo&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;15+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;€19/mo&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;50+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business&lt;/td&gt;
&lt;td&gt;€39/mo&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;100+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;€99/mo&lt;/td&gt;
&lt;td&gt;50+&lt;/td&gt;
&lt;td&gt;250+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; is a dedicated deployment tool — it does one thing (deploy code from Git) and the pricing reflects that simplicity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Buddy
&lt;/h3&gt;

&lt;p&gt;Pricing is based on seats and compute minutes. Agents are included, but Buddy is a full CI/CD platform, so you are paying for build pipelines, testing, and deployment together.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Seats&lt;/th&gt;
&lt;th&gt;Key Limits&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;€0/mo&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;300 GB-minutes, 1 concurrent pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;€29/mo&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3,000 GB-minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hyper&lt;/td&gt;
&lt;td&gt;€99/mo&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;6,000 GB-minutes, SSO&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Additional seats cost €9-29/month depending on the plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Octopus Deploy
&lt;/h3&gt;

&lt;p&gt;Pricing is based on projects, with add-ons for machines and tenants. Octopus is an enterprise release management platform — significantly more expensive than the other two.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Projects&lt;/th&gt;
&lt;th&gt;Machines&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;$0/yr&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Professional (Cloud)&lt;/td&gt;
&lt;td&gt;$4,330/yr&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;10 (add-ons: $770/yr per 10)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise (Cloud)&lt;/td&gt;
&lt;td&gt;$24,600/yr&lt;/td&gt;
&lt;td&gt;100+&lt;/td&gt;
&lt;td&gt;Custom&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each Polling Tentacle counts as a machine. Since you need one per server, costs scale linearly with your infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost for a Typical Setup
&lt;/h3&gt;

&lt;p&gt;For a team deploying to 10 servers behind a firewall:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DeployHQ&lt;/th&gt;
&lt;th&gt;Buddy&lt;/th&gt;
&lt;th&gt;Octopus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Monthly cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;€19 (Pro)&lt;/td&gt;
&lt;td&gt;€29+ (Pro)&lt;/td&gt;
&lt;td&gt;~$361 (Professional)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agents to install&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Includes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deployment only&lt;/td&gt;
&lt;td&gt;Full CI/CD&lt;/td&gt;
&lt;td&gt;Release management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When to Use Each Tool
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Choose DeployHQ Agent When:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You need to deploy to servers using FTP, SFTP, SSH, or S3 — especially mixed-protocol environments&lt;/li&gt;
&lt;li&gt;You want a single agent proxying to multiple servers with minimal infrastructure overhead&lt;/li&gt;
&lt;li&gt;You prefer an open source agent you can audit&lt;/li&gt;
&lt;li&gt;You use a separate CI/CD tool (GitHub Actions, GitLab CI, Jenkins) and need a dedicated deployment step&lt;/li&gt;
&lt;li&gt;Budget matters — it is the most affordable option for deployment-only use cases&lt;/li&gt;
&lt;li&gt;You deploy to &lt;a href="https://www.deployhq.com/blog/how-to-deploy-to-your-server-using-ssh-sftp-and-git-with-deployhq" rel="noopener noreferrer"&gt;shared hosting, cPanel, or legacy infrastructure&lt;/a&gt; alongside modern servers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choose Buddy Proxy Agent When:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You want CI/CD and deployment in one platform&lt;/li&gt;
&lt;li&gt;All your servers support SSH or SFTP (no FTP-only targets)&lt;/li&gt;
&lt;li&gt;You need build pipelines running alongside deployment&lt;/li&gt;
&lt;li&gt;Your team is already using Buddy for other CI/CD workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choose Octopus Deploy Polling Tentacle When:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You need per-server agent isolation for compliance or multi-team environments&lt;/li&gt;
&lt;li&gt;You are deploying .NET applications on Windows infrastructure&lt;/li&gt;
&lt;li&gt;You need enterprise release management features (approvals, tenanted deployments, runbooks)&lt;/li&gt;
&lt;li&gt;You have the budget and team to manage Tentacles across many servers&lt;/li&gt;
&lt;li&gt;Auditing requirements mandate per-target deployment logs and certificate-based authentication&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;All three tools solve the same problem — getting deployments through a firewall without opening inbound ports — but they target different audiences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DeployHQ&lt;/strong&gt; is the simplest path for teams that need protocol-flexible deployments with minimal infrastructure overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buddy&lt;/strong&gt; bundles deployment with CI/CD for teams that want everything in one platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Octopus Deploy&lt;/strong&gt; is enterprise release management for large-scale Windows/.NET environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are just trying to deploy code to a few servers behind a firewall, the &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; Agent is the fastest way to get there. Install one agent, whitelist your servers, and deploy — same workflow as any publicly accessible server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;Get started with DeployHQ&lt;/a&gt; — the Agent is available on all plans, including the free tier. For setup instructions, see the &lt;a href="https://www.deployhq.com/support/network-agents" rel="noopener noreferrer"&gt;Agent documentation&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Questions about deploying to firewalled servers? Reach out at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>deploymentagents</category>
      <category>deployhq</category>
      <category>buddy</category>
      <category>octopusdeploy</category>
    </item>
    <item>
      <title>How to Automate WordPress Deployments with Git (No More FTP)</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Mon, 02 Mar 2026 10:23:04 +0000</pubDate>
      <link>https://dev.to/deployhq/how-to-automate-wordpress-deployments-with-git-no-more-ftp-56h9</link>
      <guid>https://dev.to/deployhq/how-to-automate-wordpress-deployments-with-git-no-more-ftp-56h9</guid>
      <description>&lt;p&gt;If you're still deploying WordPress changes over FTP, you're one accidental overwrite away from a bad day. Manual file transfers don't track what changed, can't be rolled back, and fall apart the moment a second developer touches the project. The fix isn't complicated: put your WordPress theme and plugin code in Git, and let a deployment tool handle the rest.&lt;/p&gt;

&lt;p&gt;This guide walks through setting up automated Git-based deployments for WordPress — from structuring your repository to pushing changes to production with zero manual intervention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why FTP Deployments Break Down
&lt;/h2&gt;

&lt;p&gt;FTP served WordPress well in 2010. In 2026, it's a liability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No change history.&lt;/strong&gt; You can't answer &lt;q&gt;what changed last Tuesday?&lt;/q&gt; when something breaks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No rollback.&lt;/strong&gt; If a deployment introduces a bug, the only option is to re-upload the previous files — assuming you still have them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No collaboration.&lt;/strong&gt; Two developers editing the same theme via FTP will overwrite each other's work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No automation.&lt;/strong&gt; Every deployment requires a human clicking through FileZilla. That human will eventually drag the wrong folder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No staging environment.&lt;/strong&gt; You're deploying directly to production and hoping for the best.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Git-based deployments solve all five problems. Your code lives in a repository, every change is tracked, and deployments happen automatically when you push.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Track in Git (And What to Ignore)
&lt;/h2&gt;

&lt;p&gt;Not everything in a WordPress installation belongs in version control. The general rule: &lt;strong&gt;track code you write, ignore everything you install or generate.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended &lt;code&gt;.gitignore&lt;/code&gt; for WordPress
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# WordPress core — don't track, install via wp-cli or composer
&lt;/span&gt;/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;admin&lt;/span&gt;/
/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;includes&lt;/span&gt;/
/&lt;span class="n"&gt;wp&lt;/span&gt;-*.&lt;span class="n"&gt;php&lt;/span&gt;
/&lt;span class="n"&gt;index&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;
/&lt;span class="n"&gt;license&lt;/span&gt;.&lt;span class="n"&gt;txt&lt;/span&gt;
/&lt;span class="n"&gt;readme&lt;/span&gt;.&lt;span class="n"&gt;html&lt;/span&gt;
/&lt;span class="n"&gt;xmlrpc&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;

&lt;span class="c"&gt;# wp-content: track selectively
&lt;/span&gt;/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;content&lt;/span&gt;/&lt;span class="n"&gt;uploads&lt;/span&gt;/
/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;content&lt;/span&gt;/&lt;span class="n"&gt;upgrade&lt;/span&gt;/
/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;content&lt;/span&gt;/&lt;span class="n"&gt;cache&lt;/span&gt;/
/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;content&lt;/span&gt;/&lt;span class="n"&gt;advanced&lt;/span&gt;-&lt;span class="n"&gt;cache&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;
/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;content&lt;/span&gt;/&lt;span class="n"&gt;object&lt;/span&gt;-&lt;span class="n"&gt;cache&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;
/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;content&lt;/span&gt;/&lt;span class="n"&gt;db&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;

&lt;span class="c"&gt;# Environment and secrets
&lt;/span&gt;.&lt;span class="n"&gt;env&lt;/span&gt;
&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;config&lt;/span&gt;-&lt;span class="n"&gt;local&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;
*.&lt;span class="n"&gt;sql&lt;/span&gt;
*.&lt;span class="n"&gt;sql&lt;/span&gt;.&lt;span class="n"&gt;gz&lt;/span&gt;

&lt;span class="c"&gt;# Dependencies
&lt;/span&gt;/&lt;span class="n"&gt;vendor&lt;/span&gt;/
/&lt;span class="n"&gt;node_modules&lt;/span&gt;/

&lt;span class="c"&gt;# OS files
&lt;/span&gt;.&lt;span class="n"&gt;DS_Store&lt;/span&gt;
&lt;span class="n"&gt;Thumbs&lt;/span&gt;.&lt;span class="n"&gt;db&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What You Should Track
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wp-content/
├── themes/
│ └── your-custom-theme/ ← Track this
├── plugins/
│ └── your-custom-plugin/ ← Track this
└── mu-plugins/ ← Track this (must-use plugins)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use third-party plugins, manage them with &lt;a href="https://wpackagist.org/" rel="noopener noreferrer"&gt;Composer&lt;/a&gt; and track only the &lt;code&gt;composer.json&lt;/code&gt; and &lt;code&gt;composer.lock&lt;/code&gt; files — not the plugin source code itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Git for WordPress
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option A: Track Only Your Theme/Plugin
&lt;/h3&gt;

&lt;p&gt;The simplest approach — your Git repo contains only the code you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/your-theme
git init
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit of theme"&lt;/span&gt;
git remote add origin git@github.com:yourteam/your-wp-theme.git
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; then deploys this repo to &lt;code&gt;/var/www/html/wp-content/themes/your-theme/&lt;/code&gt; on your server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option B: Track the Entire wp-content Directory
&lt;/h3&gt;

&lt;p&gt;For projects where you manage multiple themes, plugins, and mu-plugins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/wordpress/wp-content
git init
&lt;span class="c"&gt;# Add your .gitignore first&lt;/span&gt;
git add .gitignore
git add themes/your-theme/ plugins/your-plugin/ mu-plugins/
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial wp-content structure"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option C: Full WordPress via Composer (Recommended for Teams)
&lt;/h3&gt;

&lt;p&gt;Use &lt;a href="https://roots.io/bedrock/" rel="noopener noreferrer"&gt;Roots Bedrock&lt;/a&gt; or a Composer-based WordPress setup for the most professional workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer create-project roots/bedrock your-project
&lt;span class="nb"&gt;cd &lt;/span&gt;your-project
git init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial Bedrock setup"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bedrock gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress core managed via Composer (not tracked in Git)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.env&lt;/code&gt; file for environment-specific configuration&lt;/li&gt;
&lt;li&gt;Better directory structure separating web root from application code&lt;/li&gt;
&lt;li&gt;Plugin management via &lt;code&gt;composer require wpackagist-plugin/plugin-name&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Method 1: Automated Deployments with DeployHQ
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; connects your Git repository to your server and deploys automatically on every push. No scripts to write, no CI pipeline to maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Project
&lt;/h3&gt;

&lt;p&gt;In your &lt;a href="https://deployhq.com" rel="noopener noreferrer"&gt;DeployHQ dashboard&lt;/a&gt;, create a new project and connect your GitHub, GitLab, or Bitbucket repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Add Your Server
&lt;/h3&gt;

&lt;p&gt;Add your production server using SSH (SFTP also works but SSH is faster and more reliable):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol:&lt;/strong&gt; SSH/SFTP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hostname:&lt;/strong&gt; your server IP or domain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; your deploy user (don't use root)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication:&lt;/strong&gt; SSH key (DeployHQ generates one — add the public key to your server's &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment path:&lt;/strong&gt; the target directory on your server, e.g.:

&lt;ul&gt;
&lt;li&gt;Theme only: &lt;code&gt;/var/www/html/wp-content/themes/your-theme&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Full wp-content: &lt;code&gt;/var/www/html/wp-content&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Bedrock: &lt;code&gt;/var/www/html/current&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Configure Build Steps
&lt;/h3&gt;

&lt;p&gt;Build steps run on DeployHQ's servers before files are transferred. Use them for:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install dependencies:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-dev&lt;/span&gt; &lt;span class="nt"&gt;--optimize-autoloader&lt;/span&gt;
npm ci &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Compile assets:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# If your theme uses Webpack, Vite, or similar&lt;/span&gt;
npm run production

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Add Post-Deployment SSH Commands
&lt;/h3&gt;

&lt;p&gt;After files are transferred, run commands on your server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clear WordPress object cache&lt;/span&gt;
wp cache flush &lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/www/html

&lt;span class="c"&gt;# Clear OPcache (if using PHP-FPM)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload php8.3-fpm

&lt;span class="c"&gt;# Run database migrations (if using a migration plugin)&lt;/span&gt;
wp core update-db &lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/www/html

&lt;span class="c"&gt;# Clear CDN cache (example: Cloudflare)&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer CF_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{"purge_everything":true}'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Enable Automatic Deployments
&lt;/h3&gt;

&lt;p&gt;In your project settings, enable the webhook. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; will deploy automatically every time you push to the configured branch. You can also set up different branches for different servers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt; → Production server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;staging&lt;/code&gt; → Staging server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;develop&lt;/code&gt; → Development server&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 6: Use .deployignore
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;.deployignore&lt;/code&gt; file in your repo root to exclude files that shouldn't be transferred to the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# Development files
&lt;/span&gt;&lt;span class="n"&gt;node_modules&lt;/span&gt;/
&lt;span class="n"&gt;tests&lt;/span&gt;/
.&lt;span class="n"&gt;github&lt;/span&gt;/
.&lt;span class="n"&gt;gitignore&lt;/span&gt;
&lt;span class="n"&gt;README&lt;/span&gt;.&lt;span class="n"&gt;md&lt;/span&gt;
&lt;span class="n"&gt;package&lt;/span&gt;.&lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="n"&gt;package&lt;/span&gt;-&lt;span class="n"&gt;lock&lt;/span&gt;.&lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="n"&gt;phpcs&lt;/span&gt;.&lt;span class="n"&gt;xml&lt;/span&gt;
&lt;span class="n"&gt;phpunit&lt;/span&gt;.&lt;span class="n"&gt;xml&lt;/span&gt;

&lt;span class="c"&gt;# Source files (only deploy compiled assets)
&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;/&lt;span class="n"&gt;scss&lt;/span&gt;/
&lt;span class="n"&gt;src&lt;/span&gt;/&lt;span class="n"&gt;js&lt;/span&gt;/
&lt;span class="n"&gt;webpack&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;
&lt;span class="n"&gt;vite&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps your deployment lean — only production-ready files reach the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 2: GitHub Actions + SSH
&lt;/h2&gt;

&lt;p&gt;For teams that want everything in their CI pipeline without a separate deployment tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy WordPress Theme&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build assets&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npm ci&lt;/span&gt;
          &lt;span class="s"&gt;npm run build&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy via rsync&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;burnett01/rsync-deployments@7.0.1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;switches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-avz --delete --exclude='.git' --exclude='node_modules' --exclude='src/'&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./&lt;/span&gt;
          &lt;span class="na"&gt;remote_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/www/html/wp-content/themes/your-theme/&lt;/span&gt;
          &lt;span class="na"&gt;remote_host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_HOST }}&lt;/span&gt;
          &lt;span class="na"&gt;remote_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;remote_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_PRIVATE_KEY }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Post-deployment commands&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/ssh-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_HOST }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_PRIVATE_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;wp cache flush --path=/var/www/html&lt;/span&gt;
            &lt;span class="s"&gt;sudo systemctl reload php8.3-fpm&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, but you're maintaining the pipeline yourself. When something breaks at 2am, you're debugging YAML instead of deploying a fix. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles this infrastructure for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 3: WP-CLI Deployment Scripts
&lt;/h2&gt;

&lt;p&gt;For power users who want full control via command-line scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deploy@your-server.com"&lt;/span&gt;
&lt;span class="nv"&gt;DEPLOY_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/www/html/wp-content/themes/your-theme"&lt;/span&gt;
&lt;span class="nv"&gt;BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Deploying WordPress theme ==="&lt;/span&gt;

&lt;span class="c"&gt;# Pre-flight checks&lt;/span&gt;
ssh &lt;span class="nv"&gt;$SERVER&lt;/span&gt; &lt;span class="s2"&gt;"test -d &lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deploy path missing"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Build locally&lt;/span&gt;
npm ci &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build

&lt;span class="c"&gt;# Sync files (excluding dev files)&lt;/span&gt;
rsync &lt;span class="nt"&gt;-avz&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'.git'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'node_modules'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'src/'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'tests/'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  ./ &lt;span class="nv"&gt;$SERVER&lt;/span&gt;:&lt;span class="nv"&gt;$DEPLOY_PATH&lt;/span&gt;/

&lt;span class="c"&gt;# Post-deploy&lt;/span&gt;
ssh &lt;span class="nv"&gt;$SERVER&lt;/span&gt; &lt;span class="s2"&gt;"wp cache flush --path=/var/www/html &amp;amp;&amp;amp; sudo systemctl reload php8.3-fpm"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Deployment complete ==="&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple, transparent, and portable. But it runs on your machine, so it doesn't work when you're offline, and other team members can't deploy without access to this script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling the Hard Part: Database Migrations
&lt;/h2&gt;

&lt;p&gt;WordPress doesn't have a built-in migration system like Rails or Laravel. Deploying code changes that require database schema updates needs careful handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 1: WP-CLI for Core Updates
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# After deploying new WordPress core files&lt;/span&gt;
wp core update-db &lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/www/html

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strategy 2: Migration Plugins
&lt;/h3&gt;

&lt;p&gt;Plugins like &lt;a href="https://deliciousbrains.com/wp-migrate-db-pro/" rel="noopener noreferrer"&gt;WP Migrate&lt;/a&gt; handle database syncing between environments. Run after code deployment to push schema changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 3: Custom Migration Scripts
&lt;/h3&gt;

&lt;p&gt;For custom plugins that modify the database, use WordPress's &lt;code&gt;dbDelta()&lt;/code&gt; function in your plugin activation hook, or run WP-CLI commands post-deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s1"&gt;'your_plugin_run_migrations();'&lt;/span&gt; &lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/www/html

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What NOT to Do
&lt;/h3&gt;

&lt;p&gt;Never copy the production database to staging and then deploy staging code back to production. Data flows down (production → staging), code flows up (staging → production).&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-Environment Setup
&lt;/h2&gt;

&lt;p&gt;A professional WordPress workflow uses at least three environments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Local] --&amp;gt;|git push| B[Git Repository]
    B --&amp;gt;|auto-deploy| C[Staging]
    C --&amp;gt;|manual promote| D[Production]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  In DeployHQ
&lt;/h3&gt;

&lt;p&gt;Create two servers in the same project:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Server&lt;/th&gt;
&lt;th&gt;Branch&lt;/th&gt;
&lt;th&gt;Deploy Mode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Staging&lt;/td&gt;
&lt;td&gt;&lt;code&gt;staging&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Automatic on push&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Manual (one-click) or automatic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This gives you a safety net: code deploys to staging first, you verify it works, then merge to &lt;code&gt;main&lt;/code&gt; to push to production.&lt;/p&gt;

&lt;h3&gt;
  
  
  WordPress Configuration Per Environment
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;wp-config.php&lt;/code&gt; with environment detection, or better yet, use a &lt;code&gt;.env&lt;/code&gt; file (native with Bedrock):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// wp-config.php approach&lt;/span&gt;
&lt;span class="nv"&gt;$env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WP_ENV'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="s1"&gt;'production'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'staging'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WP_DEBUG'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WP_DEBUG_LOG'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DISALLOW_FILE_MODS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  WordPress-Specific .deployignore Patterns
&lt;/h2&gt;

&lt;p&gt;Beyond the basics, these WordPress-specific exclusions keep your deployments clean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# WordPress dev/test files
&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;/
&lt;span class="n"&gt;phpunit&lt;/span&gt;.&lt;span class="n"&gt;xml&lt;/span&gt;.&lt;span class="n"&gt;dist&lt;/span&gt;
&lt;span class="n"&gt;phpcs&lt;/span&gt;.&lt;span class="n"&gt;xml&lt;/span&gt;.&lt;span class="n"&gt;dist&lt;/span&gt;
.&lt;span class="n"&gt;phpcs&lt;/span&gt;.&lt;span class="n"&gt;xml&lt;/span&gt;

&lt;span class="c"&gt;# Theme development
&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;/&lt;span class="n"&gt;scss&lt;/span&gt;/
&lt;span class="n"&gt;src&lt;/span&gt;/&lt;span class="n"&gt;js&lt;/span&gt;/
&lt;span class="n"&gt;webpack&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;
&lt;span class="n"&gt;vite&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;
&lt;span class="n"&gt;tailwind&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;
&lt;span class="n"&gt;postcss&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;
&lt;span class="n"&gt;babel&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;

&lt;span class="c"&gt;# Documentation
&lt;/span&gt;*.&lt;span class="n"&gt;md&lt;/span&gt;
&lt;span class="n"&gt;LICENSE&lt;/span&gt;
&lt;span class="n"&gt;CHANGELOG&lt;/span&gt;

&lt;span class="c"&gt;# CI/CD config (handled by DeployHQ, not needed on server)
&lt;/span&gt;.&lt;span class="n"&gt;github&lt;/span&gt;/
.&lt;span class="n"&gt;gitlab&lt;/span&gt;-&lt;span class="n"&gt;ci&lt;/span&gt;.&lt;span class="n"&gt;yml&lt;/span&gt;
&lt;span class="n"&gt;bitbucket&lt;/span&gt;-&lt;span class="n"&gt;pipelines&lt;/span&gt;.&lt;span class="n"&gt;yml&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tracking wp-config.php in Git.&lt;/strong&gt; This file contains database passwords. Use &lt;code&gt;.env&lt;/code&gt; files or environment-specific config files that are in &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploying node_modules.&lt;/strong&gt; Your build step should compile assets. Only deploy the compiled CSS/JS, never the 200MB of npm packages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using root for SSH deployments.&lt;/strong&gt; Create a dedicated deploy user with limited permissions. It should own the web directory and nothing else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skipping staging.&lt;/strong&gt; &lt;q&gt;It works on my machine&lt;/q&gt; is not a deployment strategy. Always verify on staging first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting to flush cache.&lt;/strong&gt; WordPress aggressively caches everything. A deployment without a cache clear means users see stale content.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Each Method
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;DeployHQ&lt;/th&gt;
&lt;th&gt;GitHub Actions&lt;/th&gt;
&lt;th&gt;WP-CLI Script&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;10 minutes&lt;/td&gt;
&lt;td&gt;1-2 hours&lt;/td&gt;
&lt;td&gt;30 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintenance&lt;/td&gt;
&lt;td&gt;None (managed)&lt;/td&gt;
&lt;td&gt;You maintain YAML&lt;/td&gt;
&lt;td&gt;You maintain script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rollback&lt;/td&gt;
&lt;td&gt;One-click&lt;/td&gt;
&lt;td&gt;Manual revert&lt;/td&gt;
&lt;td&gt;Manual revert&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-server&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;td&gt;DIY per environment&lt;/td&gt;
&lt;td&gt;DIY per server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team access&lt;/td&gt;
&lt;td&gt;Web dashboard&lt;/td&gt;
&lt;td&gt;Repo access required&lt;/td&gt;
&lt;td&gt;CLI access required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build steps&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;td&gt;GitHub-hosted runners&lt;/td&gt;
&lt;td&gt;Your machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;&lt;a href="https://deployhq.com/pricing" rel="noopener noreferrer"&gt;Free tier available&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Free for public repos&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Teams, agencies, multiple sites&lt;/td&gt;
&lt;td&gt;Dev teams already using GH Actions&lt;/td&gt;
&lt;td&gt;Solo devs, simple setups&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;The fastest path from FTP to automated deployments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Put your theme/plugin code in Git (10 minutes)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://deployhq.com/signup" rel="noopener noreferrer"&gt;Create a free&lt;/a&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; project (5 minutes)&lt;/li&gt;
&lt;li&gt;Connect your repo and add your server (5 minutes)&lt;/li&gt;
&lt;li&gt;Push a change and watch it deploy automatically&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. No YAML to debug, no scripts to maintain, no FTP clients to open. Your WordPress deployments now work like every other modern web project.&lt;/p&gt;




&lt;p&gt;If you run into issues, reach out to our team at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or find us on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>tutorials</category>
      <category>wordpress</category>
      <category>deployments</category>
    </item>
  </channel>
</rss>
