<?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: Eunyoung Jeong</title>
    <description>The latest articles on DEV Community by Eunyoung Jeong (@alpacax).</description>
    <link>https://dev.to/alpacax</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%2F3480620%2F7377ab1e-a0a8-4bff-9951-a53c1a18f812.png</url>
      <title>DEV Community: Eunyoung Jeong</title>
      <link>https://dev.to/alpacax</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alpacax"/>
    <language>en</language>
    <item>
      <title>Stop Using SSH Keys in GitHub Actions (Here's What to Use Instead)</title>
      <dc:creator>Eunyoung Jeong</dc:creator>
      <pubDate>Sat, 11 Oct 2025 00:45:52 +0000</pubDate>
      <link>https://dev.to/alpacax/stop-using-ssh-keys-in-github-actions-heres-what-to-use-instead-1mbe</link>
      <guid>https://dev.to/alpacax/stop-using-ssh-keys-in-github-actions-heres-what-to-use-instead-1mbe</guid>
      <description>&lt;h1&gt;
  
  
  How We Fixed SSH in GitHub Actions (Real Use Cases)
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The Problem: SSH in CI/CD Workflows
&lt;/h2&gt;

&lt;p&gt;If you're using GitHub Actions to deploy to your servers, you're probably doing something like this:&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;Execute remote SSH 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;&amp;lt;ssh-action&amp;gt;&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.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.USERNAME }}&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;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PORT }}&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;cd /app&lt;/span&gt;
      &lt;span class="s"&gt;git pull&lt;/span&gt;
      &lt;span class="s"&gt;npm install&lt;/span&gt;
      &lt;span class="s"&gt;pm2 restart app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach has worked for years, but it brings significant security and operational challenges that get worse as your infrastructure grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Risks
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Port 22 Exposed to the Internet&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every server with SSH enabled is under constant attack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bots scan for open port 22 thousands of times per day&lt;/li&gt;
&lt;li&gt;Automated brute force attempts against your SSH daemon&lt;/li&gt;
&lt;li&gt;Zero-day vulnerabilities in SSH daemon itself become attack vectors&lt;/li&gt;
&lt;li&gt;Even with fail2ban and strict firewall rules, the attack surface exists&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check your auth logs, you'll see hundreds of failed login attempts from random IPs. That's the reality of exposing SSH to the internet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full Server Access with SSH Keys&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SSH keys are all-or-nothing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grant complete access to the server, no way to restrict which commands can be executed&lt;/li&gt;
&lt;li&gt;Read-only operations require the same access level as destructive ones&lt;/li&gt;
&lt;li&gt;If a key leaks (committed to git, logged, stolen laptop), attackers have full control&lt;/li&gt;
&lt;li&gt;No built-in mechanism to limit what GitHub Actions workflows can do&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SSH Keys Scattered Across GitHub Secrets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Managing SSH keys at scale becomes exponential pain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One SSH key per server: 10 servers = 10 different keys to track&lt;/li&gt;
&lt;li&gt;Each repository needs its own copy of these secrets&lt;/li&gt;
&lt;li&gt;No centralized view of which keys have access to which servers&lt;/li&gt;
&lt;li&gt;Organizational secrets help, but you're still managing one key per server&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Operational Pain Points
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Key Rotation is a Nightmare&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you need to rotate SSH keys (security incident, compliance requirement, or just good practice):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate new key pairs for each server&lt;/li&gt;
&lt;li&gt;Update &lt;code&gt;authorized_keys&lt;/code&gt; on every server&lt;/li&gt;
&lt;li&gt;Update GitHub Secrets in every repository that deploys to those servers&lt;/li&gt;
&lt;li&gt;Hope you didn't miss anything&lt;/li&gt;
&lt;li&gt;Deal with broken deployments when you inevitably did&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a team with 10 servers and 20 repositories, this is &lt;strong&gt;hours&lt;/strong&gt; of work that nobody wants to do. So keys don't get rotated as often as they should.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No Centralized Audit Trail&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When something goes wrong in production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logs are scattered across multiple servers&lt;/li&gt;
&lt;li&gt;SSH logs show IP addresses (GitHub Actions runners), not which repo/workflow ran what&lt;/li&gt;
&lt;li&gt;Manual investigation required: "Who deployed to prod last Tuesday at 3pm?"&lt;/li&gt;
&lt;li&gt;No easy way to trace which workflow executed which commands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You end up cross-referencing GitHub Actions logs, SSH auth logs, bash history, and application logs across multiple servers. It's tedious and error-prone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-Repository Secret Management&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same SSH keys get duplicated across repositories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update one key → Update it in dozens of places&lt;/li&gt;
&lt;li&gt;Revoking access means touching multiple repositories&lt;/li&gt;
&lt;li&gt;No fine-grained control over what each repository can do&lt;/li&gt;
&lt;li&gt;Junior developer's first deploy has the same access as senior engineer's emergency fix&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Real Cost
&lt;/h3&gt;

&lt;p&gt;These aren't just theoretical concerns. Here's what actually happens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security incidents&lt;/strong&gt;: Leaked SSH keys in git history require emergency rotation across all servers and repos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broken deployments&lt;/strong&gt;: Missed secret updates during key rotation cause failed releases at 2am&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance failures&lt;/strong&gt;: Auditors ask "Who ran this command?" and you can't easily answer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational overhead&lt;/strong&gt;: DevOps teams spend hours managing SSH keys instead of building features&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security compromise&lt;/strong&gt;: Keys don't get rotated because it's too painful, increasing risk over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the worst part? &lt;strong&gt;This scales badly&lt;/strong&gt;. Double your servers, and you double your key management burden.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Alpacon
&lt;/h2&gt;

&lt;p&gt;Alpacon replaces SSH with API tokens and provides four purpose-built GitHub Actions. Here's what changes:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;SSH keys → API tokens&lt;/li&gt;
&lt;li&gt;Port 22 exposure → Outbound connection (zero open ports)&lt;/li&gt;
&lt;li&gt;Full access → Command-level ACLs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What you gain:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Command ACL (Access Control Lists)&lt;/strong&gt;: Tokens only run commands you explicitly allow, even if leaked, only permitted commands work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workspace-level token&lt;/strong&gt;: One token manages &lt;strong&gt;multiple servers&lt;/strong&gt; at once&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic audit trail&lt;/strong&gt;: Know exactly which repo/workflow accessed which server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible token management&lt;/strong&gt;: Update token permissions or rotate without touching GitHub Secrets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero open ports&lt;/strong&gt;: Outbound connection, servers never listen for incoming connections&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How It Works in Detail
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Command ACL
&lt;/h3&gt;

&lt;p&gt;Unlike SSH keys (which give full access), Alpacon tokens support fine-grained command ACLs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00vefwhf36r8fqgq85y2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00vefwhf36r8fqgq85y2.png" alt=" " width="800" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;br&gt;
When creating a token, you specify exactly which commands it can execute. Commands with operators (&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;, &lt;code&gt;||&lt;/code&gt;, &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;;&lt;/code&gt;) are blocked. This prevents command chaining or shell injection.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prevention benefits:&lt;/strong&gt;&lt;br&gt;
Even if the token leaks, attackers cannot execute unauthorized commands. The token can ONLY run commands you explicitly allowed.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Zero Exposed Ports (Outbound Connection)
&lt;/h3&gt;

&lt;p&gt;Alpacon uses a &lt;strong&gt;outbound connection&lt;/strong&gt; model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your server initiates an &lt;strong&gt;outbound&lt;/strong&gt; connection to your workspace&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No inbound ports&lt;/strong&gt; opened on your server&lt;/li&gt;
&lt;li&gt;Server is &lt;strong&gt;not discoverable&lt;/strong&gt; from the internet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Threats eliminated:&lt;/strong&gt; SSH port scanning, brute force attacks, direct connection attempts.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Centralized Audit Trail
&lt;/h3&gt;

&lt;p&gt;All GitHub Actions activity is automatically tracked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Operator (user/token) and target server&lt;/li&gt;
&lt;li&gt;Exact command executed with full output&lt;/li&gt;
&lt;li&gt;File transfers with size and timestamps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike SSH, where logs are scattered across servers, everything is centralized.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Simple Token Rotation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Alpacon:&lt;/strong&gt; Update token in workspace → Update GitHub Secrets once → Done&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSH:&lt;/strong&gt; Generate keys for each server → Update &lt;code&gt;authorized_keys&lt;/code&gt; everywhere → Update Secrets in every repo → Debug failures&lt;/p&gt;
&lt;h2&gt;
  
  
  Usage: Familiar Workflow, Better Security
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Remote Command Execution (alpacon-websh-action)
&lt;/h3&gt;

&lt;p&gt;Execute shell commands on remote servers, deployments, restarts, health checks, anything you'd do over SSH.&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;Setup Alpacon CLI&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;alpacax/alpacon-setup-action@v1.0.0&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 application&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;alpacax/alpacon-websh-action@v1.1.0&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;workspace-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ALPACON_WORKSPACE_URL }}&lt;/span&gt;
    &lt;span class="na"&gt;api-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ALPACON_API_TOKEN }}&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;your-server'&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;cd /app&lt;/span&gt;
      &lt;span class="s"&gt;git pull&lt;/span&gt;
      &lt;span class="s"&gt;npm install&lt;/span&gt;
      &lt;span class="s"&gt;pm2 restart app&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;Restart system service (with root access)&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;alpacax/alpacon-websh-action@v1.1.0&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;workspace-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ALPACON_WORKSPACE_URL }}&lt;/span&gt;
    &lt;span class="na"&gt;api-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ALPACON_API_TOKEN }}&lt;/span&gt;
    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;your-server'&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;systemctl restart nginx&lt;/span&gt;
    &lt;span class="na"&gt;as-root&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  File Transfer (alpacon-cp-action)
&lt;/h3&gt;

&lt;p&gt;Upload build artifacts, download logs, sync directories, everything you'd use SCP for.&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;Setup Alpacon CLI&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;alpacax/alpacon-setup-action@v1.0.0&lt;/span&gt;

&lt;span class="c1"&gt;# Upload files to server&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;Upload build artifacts&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;alpacax/alpacon-cp-action@v1.1.0&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;workspace-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ALPACON_WORKSPACE_URL }}&lt;/span&gt;
    &lt;span class="na"&gt;api-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ALPACON_API_TOKEN }}&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./dist/'&lt;/span&gt;
    &lt;span class="na"&gt;target-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;prod-server'&lt;/span&gt;
    &lt;span class="na"&gt;target-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/var/www/app/'&lt;/span&gt;
    &lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;# Download files from server&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;Download logs for analysis&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;alpacax/alpacon-cp-action@v1.1.0&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;workspace-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ALPACON_WORKSPACE_URL }}&lt;/span&gt;
    &lt;span class="na"&gt;api-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ALPACON_API_TOKEN }}&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/var/log/app/error.log'&lt;/span&gt;
    &lt;span class="na"&gt;target-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;prod-server'&lt;/span&gt;
    &lt;span class="na"&gt;target-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./logs/'&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;download&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  Prerequisites: Register Your Servers
&lt;/h3&gt;

&lt;p&gt;Before using these GitHub Actions, you need to register your servers with Alpacon:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a workspace&lt;/strong&gt; at &lt;a href="https://alpacon.io" rel="noopener noreferrer"&gt;alpacon.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install Alpacon agent&lt;/strong&gt; on your servers using the provided install script&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify servers are connected&lt;/strong&gt; in your workspace dashboard&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;📘 &lt;strong&gt;Full guide&lt;/strong&gt;: &lt;a href="https://docs.alpacax.com/learn/quickstart/" rel="noopener noreferrer"&gt;Alpacon Documentation - Quickstart&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once your servers are registered and showing as "CONNECTED" in your workspace, you can use them from GitHub Actions.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Generate API Token
&lt;/h3&gt;

&lt;p&gt;In your workspace settings, create an API token with appropriate command ACLs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ile1jgazhuu13gbj488.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ile1jgazhuu13gbj488.png" alt=" " width="800" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Add Secrets to GitHub
&lt;/h3&gt;

&lt;p&gt;Add these secrets to your repository (or organization for shared access):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALPACON_WORKSPACE_URL: https://alpacon.io/your-workspace
ALPACON_API_TOKEN: your-token-here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Use the Actions
&lt;/h3&gt;

&lt;p&gt;Start with &lt;code&gt;alpacon-setup-action&lt;/code&gt;, then add other actions as needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Four Actions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;alpacon-setup-action&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Install Alpacon CLI&lt;/td&gt;
&lt;td&gt;Always run first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;alpacon-websh-action&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Execute remote commands&lt;/td&gt;
&lt;td&gt;Deploy, restart services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;alpacon-cp-action&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transfer files&lt;/td&gt;
&lt;td&gt;Upload builds, download logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;alpacon-common-action&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Run any Alpacon command&lt;/td&gt;
&lt;td&gt;List servers, check status&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.alpacax.com/" rel="noopener noreferrer"&gt;Alpacon Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/marketplace/actions/setup-alpacon-cli" rel="noopener noreferrer"&gt;GitHub Marketplace - Setup Action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/marketplace/actions/alpacon-websh-action" rel="noopener noreferrer"&gt;GitHub Marketplace - Websh Action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/marketplace/actions/alpacon-cp-action" rel="noopener noreferrer"&gt;GitHub Marketplace - CP Action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/marketplace/actions/alpacon-common-action" rel="noopener noreferrer"&gt;GitHub Marketplace - Common Action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/alpacax/what-do-you-think-about-the-way-we-still-use-ssh-for-server-access-2j52"&gt;Previous Article: SSH Problems&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;SSH served us well for 30 years. But modern infrastructure demands modern solutions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API tokens&lt;/strong&gt; instead of SSH keys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Command ACLs&lt;/strong&gt; instead of full access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit logs&lt;/strong&gt; instead of manual investigation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero exposed ports&lt;/strong&gt; instead of internet-facing SSH&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centralized management&lt;/strong&gt; instead of per-repo configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alpacon GitHub Actions make these benefits accessible in your CI/CD pipelines today.&lt;/p&gt;

&lt;p&gt;Try it in your next deployment and see the difference.&lt;/p&gt;




&lt;h3&gt;
  
  
  Questions of feedback?
&lt;/h3&gt;

&lt;p&gt;We'd love to hear from you in our Discord community.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👉 Join our Discord&lt;/strong&gt;: [&lt;a href="https://discord.gg/wadWh8VsYB" rel="noopener noreferrer"&gt;https://discord.gg/wadWh8VsYB&lt;/a&gt;]&lt;br&gt;
&lt;strong&gt;or drop a comment below!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Want hands-on experience with extra rewards?
&lt;/h3&gt;

&lt;p&gt;We're running a beta testing program where you can explore GitHub Actions integration and earn rewards while helping us improve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👉 Join the beta&lt;/strong&gt;: [&lt;a href="https://forms.gle/t1rcgXgyPfZewYgF8" rel="noopener noreferrer"&gt;https://forms.gle/t1rcgXgyPfZewYgF8&lt;/a&gt;]&lt;/p&gt;

</description>
      <category>devops</category>
      <category>github</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What do you think about the way we still use SSH for server access?</title>
      <dc:creator>Eunyoung Jeong</dc:creator>
      <pubDate>Mon, 22 Sep 2025 05:52:31 +0000</pubDate>
      <link>https://dev.to/alpacax/what-do-you-think-about-the-way-we-still-use-ssh-for-server-access-2j52</link>
      <guid>https://dev.to/alpacax/what-do-you-think-about-the-way-we-still-use-ssh-for-server-access-2j52</guid>
      <description>&lt;p&gt;For decades, SSH has been the default way to access and manage servers. &lt;br&gt;
It works, it’s powerful, and it’s everywhere.&lt;br&gt;
But as our teams and infrastructure grew, we started noticing more and more friction.&lt;/p&gt;

&lt;p&gt;Some of the pain points we faced were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Managing keys and permissions across many users was messy and time-consuming&lt;/li&gt;
&lt;li&gt;Simple human errors sometimes caused outages or risky changes&lt;/li&gt;
&lt;li&gt;There was no easy way to collaborate in real-time on the same server session&lt;/li&gt;
&lt;li&gt;Security concerns grew around credential rotation and temporary access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These experiences made us rethink how server access should work.&lt;br&gt;
That’s why we started building Alpacon, a platform designed to simplify access, improve collaboration, and reduce human mistakes.&lt;br&gt;
Of course, this isn’t a problem only our team faces. Many developers, DevOps engineers, and security teams must have run into the same issues.&lt;/p&gt;

&lt;p&gt;So we’re curious: How do you feel about this?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is SSH still “good enough” for your workflow?&lt;/li&gt;
&lt;li&gt;Or do you also feel the need for something new?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’d love to hear your thoughts and keep this discussion going.&lt;/p&gt;

&lt;p&gt;To make it easier, we opened a Discord community where we share ideas about DevOps, security, and infrastructure access.&lt;br&gt;
: &lt;a href="https://discord.com/invite/wadWh8VsYB" rel="noopener noreferrer"&gt;https://discord.com/invite/wadWh8VsYB&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking forward to your perspectives!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cybersecurity</category>
      <category>ssh</category>
      <category>discuss</category>
    </item>
    <item>
      <title>discord</title>
      <dc:creator>Eunyoung Jeong</dc:creator>
      <pubDate>Mon, 22 Sep 2025 05:36:35 +0000</pubDate>
      <link>https://dev.to/alpacax/discord-2nkj</link>
      <guid>https://dev.to/alpacax/discord-2nkj</guid>
      <description></description>
    </item>
  </channel>
</rss>
