<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Arm on Yarang's Tech Lair</title><link>https://blog.fcoinfup.com/tags/arm/</link><description>Recent content in Arm on Yarang's Tech Lair</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sun, 03 May 2026 01:20:00 +0900</lastBuildDate><atom:link href="https://blog.fcoinfup.com/tags/arm/index.xml" rel="self" type="application/rss+xml"/><item><title>Building a Blog AI Auto-Comment System (3/3): Deployment and Troubleshooting</title><link>https://blog.fcoinfup.com/post/ai-auto-comment-system-part3-deployment/</link><pubDate>Sun, 03 May 2026 01:20:00 +0900</pubDate><guid>https://blog.fcoinfup.com/post/ai-auto-comment-system-part3-deployment/</guid><description>&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## Overview&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;In [Part &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;](&lt;span style="color:#f92672"&gt;/&lt;/span&gt;ko&lt;span style="color:#f92672"&gt;/&lt;/span&gt;post&lt;span style="color:#f92672"&gt;/&lt;/span&gt;ai&lt;span style="color:#f92672"&gt;-&lt;/span&gt;auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;system&lt;span style="color:#f92672"&gt;-&lt;/span&gt;part1&lt;span style="color:#f92672"&gt;-&lt;/span&gt;architecture&lt;span style="color:#f92672"&gt;/&lt;/span&gt;), we covered the architecture &lt;span style="color:#f92672"&gt;and&lt;/span&gt; implementation, &lt;span style="color:#f92672"&gt;and&lt;/span&gt; &lt;span style="color:#f92672"&gt;in&lt;/span&gt; [Part &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;](&lt;span style="color:#f92672"&gt;/&lt;/span&gt;ko&lt;span style="color:#f92672"&gt;/&lt;/span&gt;post&lt;span style="color:#f92672"&gt;/&lt;/span&gt;ai&lt;span style="color:#f92672"&gt;-&lt;/span&gt;auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;system&lt;span style="color:#f92672"&gt;-&lt;/span&gt;part2&lt;span style="color:#f92672"&gt;-&lt;/span&gt;security&lt;span style="color:#f92672"&gt;/&lt;/span&gt;), we looked at security enhancements&lt;span style="color:#f92672"&gt;.&lt;/span&gt; In this &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;rd part, we record the process of deploying to an actual OCI ARM server &lt;span style="color:#f92672"&gt;and&lt;/span&gt; the troubleshooting encountered&lt;span style="color:#f92672"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;In particular, we share &lt;span style="color:#f92672"&gt;in&lt;/span&gt; detail the actual debugging process where we tracked &lt;span style="color:#f92672"&gt;and&lt;/span&gt; resolved the issue of &lt;span style="color:#f92672"&gt;**&lt;/span&gt;GITHUB_TOKEN &lt;span style="color:#f92672"&gt;not&lt;/span&gt; loading&lt;span style="color:#f92672"&gt;**&lt;/span&gt; over &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; steps&lt;span style="color:#f92672"&gt;.&lt;/span&gt; The core of this article is how we narrowed down the cause &lt;span style="color:#f92672"&gt;in&lt;/span&gt; a situation where &lt;span style="color:#e6db74"&gt;&amp;#34;it&amp;#39;s set up, so why isn&amp;#39;t it working?&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;---&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## Infrastructure Configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;### Server Configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;|&lt;/span&gt; Server &lt;span style="color:#f92672"&gt;|&lt;/span&gt; Role &lt;span style="color:#f92672"&gt;|&lt;/span&gt; Specs &lt;span style="color:#f92672"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;|------|------|------|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;|&lt;/span&gt; ec1 (x86) &lt;span style="color:#f92672"&gt;|&lt;/span&gt; Web Server (nginx, Hugo blog) &lt;span style="color:#f92672"&gt;|&lt;/span&gt; OCI &lt;span style="color:#f92672"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;|&lt;/span&gt; arm1 (ARM) &lt;span style="color:#f92672"&gt;|&lt;/span&gt; Worker Server (Flask, Claude Code) &lt;span style="color:#f92672"&gt;|&lt;/span&gt; OCI ARM &lt;span style="color:#f92672"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;The blog is built &lt;span style="color:#f92672"&gt;and&lt;/span&gt; served with Hugo on ec1, &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; the AI comment worker runs on arm1&lt;span style="color:#f92672"&gt;.&lt;/span&gt; The GitHub Webhook is delivered directly to arm1&lt;span style="color:#f92672"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;### Worker Server Directory Structure&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;/var/www/auto-comment-worker/ # Application
├── scripts/
│ └── auto-comment-worker.py
├── deploy/
│ └── auto-comment-worker.service
├── venv/ # Python virtual environment
└── logs/&lt;/p&gt;
&lt;p&gt;/etc/auto-comment-worker/ # Credentials
├── github-token # 640, ubuntu:ubuntu
└── credentials/
└── webhook-secret # 600, ubuntu:ubuntu&lt;/p&gt;
&lt;p&gt;/home/ubuntu/.local/bin/claude # Claude Code CLI&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;---&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## systemd Service Configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;### Service File&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;```&lt;/span&gt;ini
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[Unit]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Description&lt;span style="color:#f92672"&gt;=&lt;/span&gt;Auto Comment Worker &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; Blog
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;After&lt;span style="color:#f92672"&gt;=&lt;/span&gt;network&lt;span style="color:#f92672"&gt;.&lt;/span&gt;target
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[Service]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Type&lt;span style="color:#f92672"&gt;=&lt;/span&gt;simple
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;User&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ubuntu
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WorkingDirectory&lt;span style="color:#f92672"&gt;=/&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;www&lt;span style="color:#f92672"&gt;/&lt;/span&gt;auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;worker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;PORT&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;8081&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;CLAUDE_CODE_PATH&lt;span style="color:#f92672"&gt;=/&lt;/span&gt;home&lt;span style="color:#f92672"&gt;/&lt;/span&gt;ubuntu&lt;span style="color:#f92672"&gt;/.&lt;/span&gt;local&lt;span style="color:#f92672"&gt;/&lt;/span&gt;bin&lt;span style="color:#f92672"&gt;/&lt;/span&gt;claude
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;BLOG_OWNERS&lt;span style="color:#f92672"&gt;=&lt;/span&gt;yarang
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;GITHUB_TOKEN_FILE&lt;span style="color:#f92672"&gt;=/&lt;/span&gt;etc&lt;span style="color:#f92672"&gt;/&lt;/span&gt;auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;worker&lt;span style="color:#f92672"&gt;/&lt;/span&gt;github&lt;span style="color:#f92672"&gt;-&lt;/span&gt;token
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;GITHUB_WEBHOOK_SECRET_FILE&lt;span style="color:#f92672"&gt;=/&lt;/span&gt;etc&lt;span style="color:#f92672"&gt;/&lt;/span&gt;auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;worker&lt;span style="color:#f92672"&gt;/&lt;/span&gt;credentials&lt;span style="color:#f92672"&gt;/&lt;/span&gt;webhook&lt;span style="color:#f92672"&gt;-&lt;/span&gt;secret
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ExecStart&lt;span style="color:#f92672"&gt;=/&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;www&lt;span style="color:#f92672"&gt;/&lt;/span&gt;auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;worker&lt;span style="color:#f92672"&gt;/&lt;/span&gt;venv&lt;span style="color:#f92672"&gt;/&lt;/span&gt;bin&lt;span style="color:#f92672"&gt;/&lt;/span&gt;python &lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;www&lt;span style="color:#f92672"&gt;/&lt;/span&gt;auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;worker&lt;span style="color:#f92672"&gt;/&lt;/span&gt;scripts&lt;span style="color:#f92672"&gt;/&lt;/span&gt;auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;worker&lt;span style="color:#f92672"&gt;.&lt;/span&gt;py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Restart&lt;span style="color:#f92672"&gt;=&lt;/span&gt;always
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RestartSec&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Logging&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;StandardOutput&lt;span style="color:#f92672"&gt;=&lt;/span&gt;journal
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;StandardError&lt;span style="color:#f92672"&gt;=&lt;/span&gt;journal
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;SyslogIdentifier&lt;span style="color:#f92672"&gt;=&lt;/span&gt;auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;worker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Security&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;NoNewPrivileges&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PrivateTmp&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ProtectSystem&lt;span style="color:#f92672"&gt;=&lt;/span&gt;strict
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ProtectHome&lt;span style="color:#f92672"&gt;=&lt;/span&gt;false
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ReadWritePaths&lt;span style="color:#f92672"&gt;=/&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;www&lt;span style="color:#f92672"&gt;/&lt;/span&gt;auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;worker &lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;log&lt;span style="color:#f92672"&gt;/&lt;/span&gt;auto&lt;span style="color:#f92672"&gt;-&lt;/span&gt;comment&lt;span style="color:#f92672"&gt;-&lt;/span&gt;worker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ReadOnlyPaths&lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Resource Limits&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MemoryMax&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;512&lt;/span&gt;M
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CPUQuota&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;&lt;span style="color:#f92672"&gt;%&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TasksMax&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[Install]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WantedBy&lt;span style="color:#f92672"&gt;=&lt;/span&gt;multi&lt;span style="color:#f92672"&gt;-&lt;/span&gt;user&lt;span style="color:#f92672"&gt;.&lt;/span&gt;target
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="key-configuration-explanation"&gt;Key Configuration Explanation
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;Type=simple&lt;/code&gt;&lt;/strong&gt;: Since the Flask worker runs in the foreground, &lt;code&gt;simple&lt;/code&gt; is appropriate. &lt;code&gt;forking&lt;/code&gt; is used for processes that daemonize.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;User=ubuntu&lt;/code&gt;&lt;/strong&gt;: Although a dedicated service account could be created, it runs as &lt;code&gt;ubuntu&lt;/code&gt; because the Claude Code CLI depends on the &lt;code&gt;ubuntu&lt;/code&gt; user&amp;rsquo;s home directory configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;ProtectHome=false&lt;/code&gt;&lt;/strong&gt;: Usually set to &lt;code&gt;true&lt;/code&gt;, but allows home directory access because Claude Code requires the &lt;code&gt;~/.agent_forge_for_zai.json&lt;/code&gt; configuration file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;ReadOnlyPaths=&lt;/code&gt;&lt;/strong&gt; (Empty value): Initially specified &lt;code&gt;/etc/auto-comment-worker&lt;/code&gt;, but left empty due to conflict with &lt;code&gt;ProtectSystem=strict&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="service-management-commands"&gt;Service Management Commands
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Copy service file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo cp deploy/auto-comment-worker.service /etc/systemd/system/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Register and start service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo systemctl daemon-reload
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo systemctl enable auto-comment-worker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo systemctl start auto-comment-worker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Check status&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo systemctl status auto-comment-worker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Check logs (real-time)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo journalctl -u auto-comment-worker -f
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Check recent logs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo journalctl -u auto-comment-worker --since &lt;span style="color:#e6db74"&gt;&amp;#34;10 minutes ago&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="nginx-reverse-proxy"&gt;nginx Reverse Proxy
&lt;/h2&gt;&lt;h3 id="webhook-endpoint-configuration"&gt;Webhook Endpoint Configuration
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;server&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;listen&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;443&lt;/span&gt; &lt;span style="color:#e6db74"&gt;ssl&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;server_name&lt;/span&gt; &lt;span style="color:#e6db74"&gt;your-domain.com&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# SSL Configuration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ssl_certificate&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/etc/letsencrypt/live/your-domain.com/fullchain.pem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;ssl_certificate_key&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/etc/letsencrypt/live/your-domain.com/privkey.pem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Webhook Proxy
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;location&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/webhook&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_pass&lt;/span&gt; &lt;span style="color:#e6db74"&gt;http://localhost:8081&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;Host&lt;/span&gt; $host;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;X-Real-IP&lt;/span&gt; $remote_addr;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;X-Forwarded-For&lt;/span&gt; $proxy_add_x_forwarded_for;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;X-Forwarded-Proto&lt;/span&gt; $scheme;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# GitHub Webhook signature header forwarding (Required!)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_set_header&lt;/span&gt; &lt;span style="color:#e6db74"&gt;X-Hub-Signature-256&lt;/span&gt; $http_x_hub_signature_256;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Timeout (Waiting for Claude Code response)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_read_timeout&lt;/span&gt; &lt;span style="color:#e6db74"&gt;120s&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_connect_timeout&lt;/span&gt; &lt;span style="color:#e6db74"&gt;10s&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Health check
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;location&lt;/span&gt; &lt;span style="color:#e6db74"&gt;/health&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;proxy_pass&lt;/span&gt; &lt;span style="color:#e6db74"&gt;http://localhost:8081&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;proxy_read_timeout 120s&lt;/code&gt; is set generously because the Claude Code CLI can take up to 60 seconds to generate an AI response. Since the default timeout for GitHub Webhooks is 10 seconds, asynchronous processing could be considered in practice.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="deployment-process"&gt;Deployment Process
&lt;/h2&gt;&lt;h3 id="manual-deployment-after-rsync-failure"&gt;Manual Deployment (After rsync Failure)
&lt;/h3&gt;&lt;p&gt;Initially, we attempted deployment with rsync, but it failed because the target directory did not exist on the server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rsync: [Receiver] mkdir &lt;span style="color:#e6db74"&gt;&amp;#34;/var/www/auto-comment-worker/scripts&amp;#34;&lt;/span&gt; failed:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;No such file &lt;span style="color:#f92672"&gt;or&lt;/span&gt; directory
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As an alternative, we proceeded with scp-based manual deployment:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1. Create directory on server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;sudo mkdir -p /var/www/auto-comment-worker/scripts&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;sudo chown -R ubuntu:ubuntu /var/www/auto-comment-worker&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 2. Transfer files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scp scripts/auto-comment-worker.py ubuntu@arm1:/var/www/auto-comment-worker/scripts/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;scp deploy/auto-comment-worker.service ubuntu@arm1:/tmp/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 3. Install service file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;sudo cp /tmp/auto-comment-worker.service /etc/systemd/system/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 4. Set up Python virtual environment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;cd /var/www/auto-comment-worker &amp;amp;&amp;amp; python3 -m venv venv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;cd /var/www/auto-comment-worker &amp;amp;&amp;amp; venv/bin/pip install flask flask-limiter marshmallow requests&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 5. Configure authentication files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;sudo mkdir -p /etc/auto-comment-worker/credentials&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Token file is created directly on the server (not transferred via scp)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 6. Start service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;#34;sudo systemctl daemon-reload &amp;amp;&amp;amp; sudo systemctl enable --now auto-comment-worker&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="heredoc-variable-expansion-pitfall"&gt;Heredoc Variable Expansion Pitfall
&lt;/h3&gt;&lt;p&gt;A common mistake when writing installation scripts with heredoc:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Single quotes: Variables are NOT expanded!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;lt;&amp;lt; &amp;#39;ENDSSH&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;echo $CREDENTIALS_DIR # Prints empty string
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;ENDSSH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# No quotes: Variables are expanded locally&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh ubuntu@arm1 &lt;span style="color:#e6db74"&gt;&amp;lt;&amp;lt; ENDSSH
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;echo $CREDENTIALS_DIR # Expanded to local variable value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;ENDSSH&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To avoid this problem, we switched to executing commands individually instead of using a script.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="troubleshooting-github_token-loading-failure"&gt;Troubleshooting: GITHUB_TOKEN Loading Failure
&lt;/h2&gt;&lt;p&gt;This was the issue that consumed the most time while deploying this system. When the comment Webhook arrived, the following error repeated:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;INFO:__main__:GITHUB_TOKEN configured: False
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;INFO:__main__:GitHub API response status: 401
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ERROR:__main__:Failed to get Discussion GraphQL ID
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We record the process of tracking down the cause step by step.&lt;/p&gt;
&lt;h3 id="step-1-loadcredential-path-issue"&gt;Step 1: LoadCredential Path Issue
&lt;/h3&gt;&lt;p&gt;Initially, we used the &lt;code&gt;LoadCredential&lt;/code&gt; directive in systemd:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;LoadCredential&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;github-token:/etc/auto-comment-worker/github-token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;GITHUB_TOKEN_FILE=%d/github-token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;%d&lt;/code&gt; is a systemd special variable replaced with the credentials directory path. However, this variable was not interpreted as intended, causing the token file path to be set incorrectly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Instead of &lt;code&gt;LoadCredential&lt;/code&gt;, we specified the absolute path directly.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Environment&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;GITHUB_TOKEN_FILE=/etc/auto-comment-worker/github-token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="step-2-file-ownership-issue"&gt;Step 2: File Ownership Issue
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;GITHUB_TOKEN configured: False&lt;/code&gt; still appeared. Checking the file revealed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ls -la /etc/auto-comment-worker/github-token
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw------- &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; root root &lt;span style="color:#ae81ff"&gt;93&lt;/span&gt; May &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; 01:10 github-token
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since the file owner is &lt;code&gt;root&lt;/code&gt; and permissions are &lt;code&gt;600&lt;/code&gt;, the service running as the &lt;code&gt;ubuntu&lt;/code&gt; user cannot read this file.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo chown ubuntu:ubuntu /etc/auto-comment-worker/github-token
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo chmod &lt;span style="color:#ae81ff"&gt;640&lt;/span&gt; /etc/auto-comment-worker/github-token
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="step-3-readonlypaths-conflict"&gt;Step 3: ReadOnlyPaths Conflict
&lt;/h3&gt;&lt;p&gt;Even after changing ownership, &lt;code&gt;GITHUB_TOKEN configured: False&lt;/code&gt; persisted. The cause was the &lt;code&gt;ReadOnlyPaths&lt;/code&gt; setting in the systemd service file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# This setting blocked file reading&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ReadOnlyPaths&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/etc/auto-comment-worker&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;ProtectSystem=strict&lt;/code&gt; already mounts the entire filesystem read-only. Adding &lt;code&gt;ReadOnlyPaths&lt;/code&gt; on top of that can cause mount namespace conflicts in some environments.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Changed &lt;code&gt;ReadOnlyPaths&lt;/code&gt; to an empty value.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;ReadOnlyPaths&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="step-4-python-file-permission-validation-code-root-cause"&gt;Step 4: Python File Permission Validation Code (Root Cause)
&lt;/h3&gt;&lt;p&gt;Even after resolving all previous 3 steps, the token still did not load. The final cause was the overly strict file permission validation in the Python code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# File permission 640 → Group read bit (0o040) is set → Denied!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; st&lt;span style="color:#f92672"&gt;.&lt;/span&gt;st_mode &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; (stat&lt;span style="color:#f92672"&gt;.&lt;/span&gt;S_IRWXO &lt;span style="color:#f92672"&gt;|&lt;/span&gt; stat&lt;span style="color:#f92672"&gt;.&lt;/span&gt;S_IRWXG):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;raise&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;PermissionError&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Token file must be 600 or 400&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because we changed to &lt;code&gt;chmod 640&lt;/code&gt; in Step 2, the group read bit was set, triggering this validation. However, the error message did not appear in the logs, delaying discovery — because the &lt;code&gt;PermissionError&lt;/code&gt; occurred at the module import time, preventing the service from starting at all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: As explained in Part 2, we modified it to check only &lt;code&gt;stat.S_IWOTH&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="importance-of-debugging-logs"&gt;Importance of Debugging Logs
&lt;/h3&gt;&lt;p&gt;The debugging logs added to track this issue:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;GITHUB_TOKEN configured: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;bool(GITHUB_TOKEN)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;GitHub API response status: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;status_code&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;logger&lt;span style="color:#f92672"&gt;.&lt;/span&gt;info(&lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;GitHub API response body: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;response&lt;span style="color:#f92672"&gt;.&lt;/span&gt;text[:&lt;span style="color:#ae81ff"&gt;500&lt;/span&gt;]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Without these logs, it would have taken much longer to identify the cause. Always log token load success/failure and API response status for authentication-related code.&lt;/p&gt;
&lt;h3 id="debugging-flow-summary"&gt;Debugging Flow Summary
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-zed" data-lang="zed"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#960050;background-color:#1e0010"&gt;1&lt;/span&gt;] LoadCredential &lt;span style="color:#f92672"&gt;%&lt;/span&gt;d not interpreted &lt;span style="color:#960050;background-color:#1e0010"&gt;→&lt;/span&gt; Changed to absolute path
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;↓&lt;/span&gt; (Still failed)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#960050;background-color:#1e0010"&gt;2&lt;/span&gt;] File owner root&lt;span style="color:#f92672"&gt;:&lt;/span&gt;root &lt;span style="color:#960050;background-color:#1e0010"&gt;→&lt;/span&gt; Changed to ubuntu&lt;span style="color:#f92672"&gt;:&lt;/span&gt;ubuntu
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;↓&lt;/span&gt; (Still failed)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#960050;background-color:#1e0010"&gt;3&lt;/span&gt;] ReadOnlyPaths conflict &lt;span style="color:#960050;background-color:#1e0010"&gt;→&lt;/span&gt; Removed
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;↓&lt;/span&gt; (Still failed)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#960050;background-color:#1e0010"&gt;4&lt;/span&gt;] Python &lt;span style="color:#66d9ef"&gt;permission&lt;/span&gt; check S_IRWXG &lt;span style="color:#960050;background-color:#1e0010"&gt;→&lt;/span&gt; Relaxed to S_IWOTH
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;↓&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [Resolved&lt;span style="color:#f92672"&gt;!&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Lessons learned from this 4-step debugging:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Change one at a time and verify&lt;/strong&gt;: If you change multiple settings at once, you won&amp;rsquo;t know which one is the cause.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trust logs, but suspect where there are no logs&lt;/strong&gt;: Exceptions at module load time may not appear in standard logs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security validation code can also be a source of bugs&lt;/strong&gt;: When security code blocks normal operation — balancing security and operations.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="health-check"&gt;Health Check
&lt;/h2&gt;&lt;p&gt;A health check endpoint to verify the service is running correctly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@app.route&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#39;/health&amp;#39;&lt;/span&gt;, methods&lt;span style="color:#f92672"&gt;=&lt;/span&gt;[&lt;span style="color:#e6db74"&gt;&amp;#39;GET&amp;#39;&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;health&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&amp;#34;Health check&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; jsonify({&lt;span style="color:#e6db74"&gt;&amp;#39;status&amp;#39;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#39;healthy&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Monitoring systems periodically call &lt;code&gt;/health&lt;/code&gt; to verify the service status:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -s http://localhost:8081/health
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# {&amp;#34;status&amp;#34;: &amp;#34;healthy&amp;#34;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="future-improvements"&gt;Future Improvements
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Asynchronous Processing&lt;/strong&gt;: Asynchronize AI response generation using Celery or Redis Queue to respond within the GitHub Webhook timeout (10 seconds).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retry Logic&lt;/strong&gt;: Exponential backoff retry on GitHub API call failures.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Monitoring Dashboard&lt;/strong&gt;: Monitor response time, success rate, and error rate with Prometheus + Grafana.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automated Deployment&lt;/strong&gt;: Build an automated deployment pipeline with GitHub Actions on code changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Testing&lt;/strong&gt;: Write integration tests mocking Webhook payloads.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="conclusion"&gt;Conclusion
&lt;/h2&gt;&lt;p&gt;Over three parts, we have recorded the entire build process of the blog AI auto-comment system:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1&lt;/strong&gt;: Full architecture of giscus → GitHub Webhook → Flask → Claude Code → GraphQL&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2&lt;/strong&gt;: File-based authentication, HMAC verification, input sanitization, systemd security&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 3&lt;/strong&gt;: Actual deployment, nginx proxy, 4-step debugging process&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The greatest value of this system is that it &lt;strong&gt;automates communication with blog readers&lt;/strong&gt;. While it is difficult for blog operators to respond to every comment immediately, an AI assistant can provide a first response, improving the reader experience.&lt;/p&gt;
&lt;p&gt;The full code is available at the &lt;a class="link" href="https://github.com/yarang/blogs" target="_blank" rel="noopener"
 &gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This article is Part 3 (the final part) of the AgentForge blog auto-comment system series.&lt;/em&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item></channel></rss>