<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.tellsiddh.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.tellsiddh.com/" rel="alternate" type="text/html" /><updated>2025-11-01T20:00:08+00:00</updated><id>https://blog.tellsiddh.com/feed.xml</id><title type="html">tellsiddh’s blog</title><subtitle>A simple blog powered by Jekyll</subtitle><author><name>siddharth</name></author><entry><title type="html">Oct 31st – Tripped Circuit Breaker for Vector Database</title><link href="https://blog.tellsiddh.com/database/vector-database-circuit-breaker/" rel="alternate" type="text/html" title="Oct 31st – Tripped Circuit Breaker for Vector Database" /><published>2025-10-31T00:00:00+00:00</published><updated>2025-10-31T00:00:00+00:00</updated><id>https://blog.tellsiddh.com/database/vector-database-circuit-breaker</id><content type="html" xml:base="https://blog.tellsiddh.com/database/vector-database-circuit-breaker/"><![CDATA[<p>Our vector database of choice is the <strong>AWS OpenSearch Service</strong>. <strong>October 31st</strong>, we encountered an unexpected challenge, our OpenSearch cluster tripped a <strong>circuit breaker</strong> due to high memory usage.</p>

<!--more-->

<h2 id="background">Background</h2>

<p>We rely on OpenSearch’s <strong>k-NN plugin</strong> (built on FAISS) for vector search.<br />
Each index contains high-dimensional embeddings (~1536 dimensions), and we ingest data in <strong>bulk batches of 256 documents</strong> at a time.</p>

<p>Our setup:</p>

<ul>
  <li><strong>Engine:</strong> OpenSearch_2.19</li>
  <li><strong>Instance type:</strong> <code class="language-plaintext highlighter-rouge">or2.large.search</code></li>
  <li><strong>Data nodes:</strong> 6 → later scaled to 9</li>
  <li><strong>Breaker limit:</strong> 60% (default)</li>
  <li><strong>Memory usage pattern:</strong> One index (<code class="language-plaintext highlighter-rouge">mab_b2f97...</code>) consistently dominated memory.</li>
</ul>

<hr />

<h2 id="the-circuit-breaker-event">The Circuit Breaker Event</h2>

<p>During a bulk ingestion job, we started seeing errors like this:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">"caused_by"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"knn_circuit_breaker_exception"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"reason"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Parsing the created knn vector fields prior to indexing has failed as the circuit breaker triggered. This indicates that the cluster is low on memory resources and cannot index more documents at the moment. Check _plugins/_knn/stats for the circuit breaker status."</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h2 id="running-the-diagnostic-command">Running the diagnostic command:</h2>

<p>GET _plugins/_knn/stats?pretty</p>

<p>produced the following key result:</p>

<p>“circuit_breaker_triggered”: true</p>

<p>Two nodes had excessive graph memory usage:</p>

<p>Node ID	Graph Memory Usage	Cache Full	Top Index	Usage %
rvr4O1HFRiiZpcO1BctWK61	98.27%	True	mab_b2f97…	85.95%
knev23f3029vhivh13fjevn	91.21%	False	mab_b2f97…	84.24%</p>

<p>This confirmed that the breaker was triggered by one overloaded index that consumed almost all available native memory on a single data node.</p>

<p><strong>Troubleshooting Process</strong></p>

<p>We built a small Python diagnostic script to confirm the issue programmatically.</p>

<ul>
  <li>Checking k-NN Stats
import requests
import boto3, json</li>
</ul>

<p>region = ‘us-west-2’
service = ‘es’</p>

<p>endpoint = “https://vpc-main-xxxx.us-west-2.es.amazonaws.com”
url = f”{endpoint}/_plugins/_knn/stats?pretty”</p>

<p>resp = requests.get(url)
print(resp.status_code)
print(json.dumps(resp.json(), indent=2))</p>

<p>This returned detailed per-node memory usage, helping us pinpoint the culprit.</p>

<p><strong>Verifying Breaker Limit</strong>
url = f”{endpoint}/_cluster/settings?include_defaults=true&amp;filter_path=**.indices.breaker.request.limit”
resp = requests.get(url)
print(json.dumps(resp.json(), indent=2))</p>

<p>Result:</p>

<p>{
  “defaults”: {
    “indices”: {
      “breaker”: {
        “request”: {
          “limit”: “60%”
        }
      }
    }
  }
}</p>

<p>So, we were indeed running close to the default 60% circuit breaker threshold.</p>

<ul>
  <li>Checking Cluster Health
url = f”{endpoint}/_cluster/health?pretty”
resp = requests.get(url)
print(json.dumps(resp.json(), indent=2))</li>
</ul>

<p>Output:</p>

<p>“status”: “green”,
“relocating_shards”: 2,
“active_shards_percent_as_number”: 100.0</p>

<p>Everything was healthy — except for the memory issue.</p>

<h2 id="diagnosing-shard-distribution">Diagnosing Shard Distribution</h2>

<p>We then checked how the heavy index shards were distributed across nodes:</p>

<p>url = f”{endpoint}/_cat/shards?v&amp;format=json”
response = requests.get(url)
shards = [s for s in response.json() if “mab_b2f97” in s[“index”]]</p>

<p>for s in shards:
    print(f”Index: {s[‘index’]} | Shard: {s[‘shard’]} | Node: {s[‘node’]}”)</p>

<p>Output:</p>

<p>Found 10 shards for index pattern ‘mab_b2f97’:
  Shard 0 → 234rsvdsvde43t36r2b14b686b19523f, b464vwerbwvev393849t8vb97ff1f554
  Shard 1 → 234rsvdsvde43t36r2b14b686b19523f, 4289fhf83fb3vb3839c36b17afaf590e
  Shard 2 → 234rsvdsvde43t36r2b14b686b19523f, 9b90871abd6e7r3if32iv209vnooino9
  …</p>

<p>⚠️ Observation: One node was hosting four shards of the heavy index.
That node was exactly the one showing 98% memory usage.</p>

<h3 id="fix-attempt-1--scaling-data-nodes">Fix Attempt 1 – Scaling Data Nodes</h3>

<p>We increased the number of data nodes from 6 → 9.
The cluster started redistributing shards (relocating_shards: 2).
However, after waiting ~30 minutes, the breaker still remained tripped — because the overloaded node reloaded the same k-NN graphs into memory.</p>

<h3 id="fix-attempt-2--reboot-node">Fix Attempt 2 – Reboot Node</h3>

<p>We followed AWS docs and rebooted the data node via the console.
Result: Node restarted successfully, Circuit breaker still tripped.</p>

<p>Rebooting cleared the JVM heap, but as soon as the node rejoined, OpenSearch reloaded the same graphs, instantly hitting the memory limit again.</p>

<h3 id="fix-attempt-3--close-and-reopen-index">Fix Attempt 3 – Close and Reopen Index</h3>

<p>Finally, this worked perfectly.</p>

<p>We used the following Python script:</p>

<p>import requests, boto3, json, time</p>

<p>endpoint = “https://vpc-main-xxxx.us-west-2.es.amazonaws.com”
index_name = “mab_b2f97…”</p>

<h1 id="close-index">Close index</h1>
<p>print(f”Closing index: {index_name}”)
resp = requests.post(f”{endpoint}/{index_name}/_close”)
print(“Status:”, resp.status_code)</p>

<p>time.sleep(30)</p>

<h1 id="reopen-index">Reopen index</h1>
<p>print(f”Reopening index: {index_name}”)
resp = requests.post(f”{endpoint}/{index_name}/_open”, auth=awsauth)
print(“Status:”, resp.status_code)</p>

<h1 id="check-health">Check health</h1>
<p>resp = requests.get(f”{endpoint}/_cluster/health/{index_name}?pretty”, auth=awsauth)
print(json.dumps(resp.json(), indent=2))</p>

<p>Result:</p>

<p>Index closed successfully.
Index reopened successfully.
    Checking index health…
“status”: “yellow” → “green”</p>

<p>Then rerunning:</p>

<p>GET _plugins/_knn/stats?pretty</p>

<p>showed:</p>

<p>“circuit_breaker_triggered”: false
No nodes above 80% graph memory usage.</p>

<p>The breaker reset cleanly, and indexing resumed.</p>

<h2 id="root-cause">Root Cause</h2>

<p>One vector index (mab_b2f97…) consumed ~85% of native graph memory on a single node.</p>

<p>The k-NN circuit breaker tripped at 60% limit.</p>

<p>Adding data nodes helped distribute shards but didn’t clear the memory state automatically.</p>

<p>Rebooting nodes reloaded the same graphs.</p>

<p>Closing and reopening the index successfully flushed and rebalanced the k-NN cache.</p>

<ul>
  <li>Outcome</li>
</ul>

<p>Circuit breaker cleared</p>

<p>No nodes &gt; 80% memory usage</p>

<p>Indexing &amp; search restored</p>

<p>Cluster status: green</p>

<h2 id="lessons-learned">Lessons Learned</h2>

<p>Monitor _plugins/_knn/stats regularly.
It gives visibility into node-level memory pressure.</p>

<p>Circuit breakers protect your cluster.
They prevent out-of-memory crashes but can pause indexing.</p>

<p>Closing &amp; reopening an index is a safe AWS-native reset.
It flushes the FAISS graphs from native memory.</p>

<p>Scaling helps, but placement matters.
Use _cat/shards to ensure balanced shard distribution.</p>

<p>Default circuit breaker limit (60%) is conservative.
It’s safer to scale horizontally than tweak that threshold.</p>

<h2 id="next-steps">Next Steps</h2>

<p>We’ll continue to:</p>

<p>Monitor k-NN stats and shard placement daily.</p>

<p>Automate alerts if any node exceeds 80% graph memory usage.</p>

<p>Optimize ingestion batch size to reduce vector memory bursts.</p>

<p>Document this process in our internal runbook.</p>

<p><strong>– Siddharth</strong></p>]]></content><author><name>Siddharth Jain</name></author><category term="database" /><summary type="html"><![CDATA[Our vector database of choice is the AWS OpenSearch Service. October 31st, we encountered an unexpected challenge, our OpenSearch cluster tripped a circuit breaker due to high memory usage.]]></summary></entry><entry><title type="html">July 3rd – How We Lost Our Vector Database (and Recovered)</title><link href="https://blog.tellsiddh.com/database/vector-database-broke/" rel="alternate" type="text/html" title="July 3rd – How We Lost Our Vector Database (and Recovered)" /><published>2025-07-04T00:00:00+00:00</published><updated>2025-07-04T00:00:00+00:00</updated><id>https://blog.tellsiddh.com/database/vector-database-broke</id><content type="html" xml:base="https://blog.tellsiddh.com/database/vector-database-broke/"><![CDATA[<p>Our vector database of choice is the OpenSearch service. We initially used AWS’s serverless instance to power our Retrieval-Augmented Generation (RAG) applications. However, slow ingestion speeds led us to migrate to a managed cluster setup.</p>

<!--more-->

<h2 id="update-july-22-2025">Update: July 22, 2025</h2>

<p>We received the root cause analysis from AWS support. They confirmed that the issue was indeed caused by the simultaneous scaling and security changes. The domain was stuck in a modifying state due to simultaneous configuration changes: enabling both remote store and FGAC. They confirmed that this combination can cause a red index status for one of the system indices. The issue was resolved by deleting the affected system index and retrying the process. They said they will be implementing fixes to prevent similar incidents in the future.</p>

<p>This was a huge relief as it confirmed our suspicions and provided us with a clear path forward. We will be implementing the recommended changes in our future deployments.</p>

<p>“Until then, we recommend following this sequence: Enable FGAC first and then proceed with remote store migration if needed.” ~ AWS Support</p>

<p>This is honestly a good recommendation and I should have followed it with or without the AWS support confirmation. During any migrations or major changes, it is always a good idea to follow a sequence of steps that are known to work. This will help you avoid any unexpected issues and ensure that your changes are applied successfully. I am glad that the AWS support team was able to get us out of this situation quickly and efficiently with no data loss.</p>

<h2 id="update-july-5-2025">Update: July 5, 2025</h2>

<p>AWS support confirmed that the our cluster has fully recovered and is now in a healthy state. They also confirmed that the migration was now fully completed and all indices are accessible. We were able to confirm the same by visually looking at the cluster status. We now needed to test the cluster and make a decision if we wanted to point our pipelines to this cluster or not.</p>

<p>Waiting for AWS support to tell us if they can sync the two clusters for us - our old one and the new one. If they can, we will be able to point our pipelines to the new cluster without losing any data. If not, we will have to reingest the data from S3 again. I also needed approvals from my Director if we wanted to point our pipelines back to the old cluster or not.</p>

<p>So we wait…</p>

<h2 id="update-july-4-2025">Update: July 4, 2025</h2>

<p>AWS support identified a stuck snapshot blocking shard relocation and requested approval to perform a rolling restart of data nodes in the blue environment. After getting our approval for potential outages (since we had a backup domain which was now being used actively), they successfully restarted the nodes and confirmed the cluster returned to a healthy green state with all indices accessible.</p>

<p>Upon viewing the cluster status, I confirmed that the cluster, although in the green state, was still trying to apply some changes.</p>

<p><img src="/assets/images/cluster_latest_state_green.png" alt="Cluster Status" /></p>

<h1 id="tldr">TL;DR</h1>

<ul>
  <li><strong>Incident Date:</strong> July 3, 2025</li>
  <li><strong>Duration:</strong> ~5 hours</li>
  <li><strong>Impact:</strong> Production OpenSearch cluster became inaccessible due to simultaneous scaling and security changes.</li>
  <li><strong>Speculations:</strong> Without knowing for sure or AWS confirming, I will be mentioning if something is a pure speculation from my end. I have asked AWS for a root cause analysis and will update this post once I receive it.</li>
</ul>

<h2 id="how-it-started">How It Started</h2>

<p>During the week of June 30th, we noticed extreme JVM pressure and high CPU utilization in our Test environment. Our setup had:</p>

<ul>
  <li><strong>5 shards per index</strong> (default)</li>
  <li><strong>3 nodes</strong> with 1 dedicated master node</li>
  <li><strong>Warm storage enabled</strong></li>
  <li><strong>Document ingestion rate:</strong> ~100 docs/sec</li>
</ul>

<p>Our initial configuration looked like this:</p>

<div class="language-hcl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">cluster_config</span> <span class="err">=</span> <span class="p">{</span>
  <span class="nx">instance_count</span>           <span class="p">=</span> <span class="mi">3</span>
  <span class="nx">dedicated_master_enabled</span> <span class="p">=</span> <span class="kc">true</span>
  <span class="nx">dedicated_master_type</span>    <span class="p">=</span> <span class="s2">"c7g.large.search"</span>
  <span class="nx">instance_type</span>            <span class="p">=</span> <span class="s2">"r7g.large.search"</span>
  <span class="nx">warm_count</span>               <span class="p">=</span> <span class="mi">3</span>
  <span class="nx">warm_enabled</span>             <span class="p">=</span> <span class="kc">true</span>
  <span class="nx">warm_type</span>                <span class="p">=</span> <span class="s2">"ultrawarm1.large.search"</span>
  <span class="nx">cold_storage_options</span> <span class="p">=</span> <span class="p">{</span>
    <span class="nx">enabled</span> <span class="p">=</span> <span class="kc">false</span>
  <span class="p">}</span>
  <span class="nx">zone_awareness_config</span> <span class="p">=</span> <span class="p">{</span>
    <span class="nx">availability_zone_count</span> <span class="p">=</span> <span class="mi">3</span>
  <span class="p">}</span>
  <span class="nx">zone_awareness_enabled</span> <span class="p">=</span> <span class="kc">true</span>
<span class="p">}</span>
<span class="nx">ebs_options</span> <span class="err">=</span> <span class="p">{</span>
  <span class="nx">ebs_enabled</span> <span class="p">=</span> <span class="kc">true</span>
  <span class="nx">iops</span>        <span class="p">=</span> <span class="mi">3000</span>
  <span class="nx">throughput</span>  <span class="p">=</span> <span class="mi">250</span>
  <span class="nx">volume_type</span> <span class="p">=</span> <span class="s2">"gp3"</span>
  <span class="nx">volume_size</span> <span class="p">=</span> <span class="mi">512</span>
<span class="p">}</span>
<span class="nx">encrypt_at_rest</span> <span class="err">=</span> <span class="p">{</span>
  <span class="nx">enabled</span> <span class="p">=</span> <span class="kc">true</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This setup, while memory-optimized, was not ready for our load tests (~100 documents in short bursts). JVM pressure spiked and we needed to scale — fast.</p>

<hr />

<h2 id="scaling-up">Scaling Up</h2>

<p>I rolled out a new configuration on <strong>July 1st</strong>, increasing node count, instance size, and enabling cold storage:</p>

<div class="language-hcl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">cluster_config</span> <span class="err">=</span> <span class="p">{</span>
  <span class="nx">instance_count</span>           <span class="p">=</span> <span class="mi">9</span>
  <span class="nx">dedicated_master_enabled</span> <span class="p">=</span> <span class="kc">true</span>
  <span class="nx">dedicated_master_count</span>   <span class="p">=</span> <span class="mi">3</span>
  <span class="nx">dedicated_master_type</span>    <span class="p">=</span> <span class="s2">"r7g.large.search"</span>
  <span class="nx">instance_type</span>            <span class="p">=</span> <span class="s2">"or2.2xlarge.search"</span>
  <span class="nx">warm_count</span>               <span class="p">=</span> <span class="mi">3</span>
  <span class="nx">warm_enabled</span>             <span class="p">=</span> <span class="kc">true</span>
  <span class="nx">warm_type</span>                <span class="p">=</span> <span class="s2">"ultrawarm1.large.search"</span>
  <span class="nx">cold_storage_options</span> <span class="p">=</span> <span class="p">{</span>
    <span class="nx">enabled</span> <span class="p">=</span> <span class="kc">true</span>
  <span class="p">}</span>
  <span class="nx">zone_awareness_config</span> <span class="p">=</span> <span class="p">{</span>
    <span class="nx">availability_zone_count</span> <span class="p">=</span> <span class="mi">3</span>
  <span class="p">}</span>
  <span class="nx">zone_awareness_enabled</span> <span class="p">=</span> <span class="kc">true</span>
<span class="p">}</span>
<span class="nx">ebs_options</span> <span class="err">=</span> <span class="p">{</span>
  <span class="nx">ebs_enabled</span> <span class="p">=</span> <span class="kc">true</span>
  <span class="nx">iops</span>        <span class="p">=</span> <span class="mi">3000</span>
  <span class="nx">throughput</span>  <span class="p">=</span> <span class="mi">250</span>
  <span class="nx">volume_type</span> <span class="p">=</span> <span class="s2">"gp3"</span>
  <span class="nx">volume_size</span> <span class="p">=</span> <span class="mi">1024</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This resulted in a major performance improvement: JVM pressure dropped, CPU stabilized, and ingestion was smooth.</p>

<hr />

<h2 id="the-setup-in-production">The Setup in Production</h2>

<p>Confident in the new setup, I decided to apply the same configuration to production on <strong>July 3rd</strong>. However, there was one (SPECULATION: there might be more than one difference) <strong>critical difference</strong>:</p>

<ul>
  <li>In <strong>test</strong>, we applied configuration and security changes <strong>separately</strong>.</li>
  <li>In <strong>production</strong>, we applied <strong>both</strong> at the same time.</li>
</ul>

<hr />

<h2 id="why-security-changes">Why Security Changes?</h2>

<p>We had observed random index creations in test and wanted to enable <strong>AUDIT_LOGS</strong> to track user activity. That required enabling <strong>advanced security</strong>, which in turn required a <strong>master user</strong>. Advanced security also provides features like fine-grained access control (FGAC) and internal user database management.</p>

<div class="language-hcl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">advanced_security_options</span> <span class="err">=</span> <span class="p">{</span>
  <span class="nx">enabled</span>                        <span class="p">=</span> <span class="kc">true</span>
  <span class="nx">internal_user_database_enabled</span> <span class="p">=</span> <span class="kc">false</span>
  <span class="nx">anonymous_auth_enabled</span>         <span class="p">=</span> <span class="kc">false</span>
  <span class="nx">master_user_options</span> <span class="p">=</span> <span class="p">{</span>
    <span class="nx">master_user_arn</span> <span class="p">=</span> <span class="s2">"arn:aws:iam::xxxxxxxxxx:role/user-role"</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In the test environment, this change caused a temporary <strong>“RED” cluster state</strong> for ~10 minutes, which reverted to <strong>“GREEN”</strong> successfully. Per AWS docs:</p>

<blockquote>
  <p><em>“The change triggers a blue/green deployment during which the cluster health becomes red, but all cluster operations remain unaffected.”</em><br />
— <a href="https://docs.aws.amazon.com/opensearch-service/latest/developerguide/fgac.html#fgac-enabling">AWS Docs on FGAC</a></p>
</blockquote>

<p>So, I felt confident rolling the same changes into prod.</p>

<hr />

<h2 id="the-deployment--what-went-wrong">The Deployment – What Went Wrong?</h2>

<p>Once approved, we deployed changes in prod — both the <strong>infrastructure scale-up</strong> and <strong>advanced security enablement</strong> — simultaneously. This was the beginning of the outage (SPECULATION: we do not know for sure if this was the beginning). Below is an image of our cluster migration status during the deployment.
<img src="/assets/images/cluster_migration_1.png" alt="Cluster Migration Status" /></p>

<p>It showed that new nodes had been added and traffic routing was successful. It had now reached the point where it was supposed to copy the shards to the new nodes. This is when we started noticing issues. The cluster status turned to “RED”. We were unable to access the cluster and the OpenSearch dashboard was not loading. Any index or search operation was failing with a “503 Service Unavailable” error.</p>

<p>A quick google search led me to this forum post on AWS. https://repost.aws/knowledge-center/opensearch-domain-stuck-processing
The post basically mentioned that the cluster was stuck in a state where it was trying to copy the shards to the new nodes, but it was unable to do so. It asked us to monitor the current migration of the shard with the following API call:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GET /DOMAIN_ENDPOINT/_cat/recovery?active_only<span class="o">=</span><span class="nb">true</span>
</code></pre></div></div>

<p>We hit this error:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"error"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"security_exception"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"reason"</span><span class="p">:</span><span class="w"> </span><span class="s2">"OpenSearch Security not initialized for indices:monitor/recovery"</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"status"</span><span class="p">:</span><span class="w"> </span><span class="mi">503</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>That’s when I realized: <strong>the security plugin hadn’t initialized properly</strong>.</p>

<p>I attempted master-user access via IAM — still failed.</p>

<p>This was around 3:00 pm.</p>

<hr />

<h2 id="aws-support-weighs-in">AWS Support Weighs In</h2>

<p>At 6:15 pm I contacted AWS support. They were able to confirm that the cluster was stuck in a state where it was trying to copy the shards to the new nodes, but it was unable to do so. They initially mentioned that this is a normal behavior and the cluster would eventually recover. However, after waiting for a few hours, the cluster was still in the same state and we were unable to access it.</p>

<p>AWS confirmed:</p>

<ul>
  <li>The cluster was stuck during <strong>shard copying</strong> to the new nodes.</li>
  <li>Specifically, the <code class="language-plaintext highlighter-rouge">.opendistro_security</code> index — which stores security config — <strong>could not be assigned or migrated</strong>.</li>
</ul>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"index"</span><span class="p">:</span><span class="w"> </span><span class="s2">".opendistro_security"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"shard"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w">
  </span><span class="nl">"primary"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
  </span><span class="nl">"current_state"</span><span class="p">:</span><span class="w"> </span><span class="s2">"unassigned"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"unassigned_info"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"reason"</span><span class="p">:</span><span class="w"> </span><span class="s2">"INDEX_CREATED"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"at"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2025-07-03T21:27:11.736Z"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>This was the index that was causing the issue (SPECULATION: but semi confirmed by aws on the chime call). The support team tried to reassign the shard, but it was still failing with the same error. This was an internal AWS index that is used to store the security configuration for the cluster. It was not able to migrate to the new nodes and was causing the cluster to be stuck in a state where it was unable to access any indices.</p>

<hr />

<h2 id="no-way-out">No Way Out</h2>

<p>I had a few options at this point. I could either wait for the cluster to recover on its own, or abandon that cluster and create a new one. I decided to wait for a few more hours, but the cluster was still in the same state. I was unable to access any indices or perform any operations on the cluster. I confirmed that all data nodes were migrated and in active state.</p>

<p><img src="/assets/images/data_nodes.png" alt="Cluster Nodes" /></p>

<p>I explored every workaround:</p>

<ul>
  <li><strong>Reassign shards manually?</strong> Blocked.</li>
  <li><strong>Cancel update?</strong> Not allowed mid-migration.</li>
  <li><strong>Delete the domain?</strong> Domain was locked in <code class="language-plaintext highlighter-rouge">Processing</code>.</li>
  <li><strong>Restore from snapshot?</strong> AWS’s default 1-hour snapshots are not user-restorable.</li>
</ul>

<p>At this point, I was stuck in limbo.</p>

<p>Then we found this <a href="https://repost.aws/questions/QUe_bYRWWNTJ23Y9l6Rhhg6w/opensearch-service-how-to-restore-opendistro-security-index">AWS forum post from 2 years ago</a> describing <strong>exactly the same scenario</strong>. The same <code class="language-plaintext highlighter-rouge">.opendistro_security</code> index was unassignable. Unfortunately, AWS support didn’t accept it as conclusive evidence and continued investigating.</p>

<hr />

<h2 id="our-recovery-plan">Our Recovery Plan</h2>

<p>With production deadlocked, we took matters into our own hands:</p>

<ul>
  <li><strong>Spun up a new domain</strong>, same configuration — <strong>excluding</strong> advanced security.</li>
  <li><strong>Reingested from S3</strong>, our source of truth.</li>
  <li>Redirected traffic to the new cluster.</li>
</ul>

<p>Within a few hours, ingestion was back at 100 docs/sec. The new cluster held up well.</p>

<p>Meanwhile, our old cluster was still at <strong>71% migration</strong> and remained unusable but now with status <code class="language-plaintext highlighter-rouge">yellow</code>. AWS confirmed that shard migration was partially retriggered — but that didn’t help us regain access.</p>

<hr />

<h2 id="downtime-summary">Downtime Summary</h2>

<ul>
  <li><strong>Total outage</strong>: ~5 hours</li>
  <li><strong>User impact</strong>: Minimal (due to long weekend)</li>
  <li><strong>Fallback</strong>: Temporary reroute to serverless instance for select users</li>
</ul>

<hr />

<h2 id="lessons-learned">Lessons Learned</h2>

<h3 id="1-dont-combine-major-changes">1. <strong>Don’t Combine Major Changes</strong></h3>
<p>Scaling and security updates should be rolled out in <strong>separate phases</strong> — especially in production. Always perform a dry run to test the configuration first.</p>

<h3 id="2-be-cautious-with-advanced-security">2. <strong>Be Cautious with Advanced Security</strong></h3>
<p>Once enabled, <strong>you can’t disable</strong> advanced security. Enable it only when you’re confident it won’t block critical operations.</p>

<h3 id="3-snapshots-matter">3. <strong>Snapshots Matter</strong></h3>
<p>Relying on AWS’s default hourly snapshots isn’t enough. Set up <strong>manual, restorable snapshots</strong>. You cannot restore from AWS’s default snapshots directly, so ensure you have a backup strategy that allows you to restore data when needed.</p>

<h3 id="4-monitoring-is-key">4. <strong>Monitoring is Key</strong></h3>
<p>Track migrations closely. If a cluster update takes more than <strong>90 minutes</strong>, escalate immediately.</p>

<h3 id="5-have-a-rollback-plan">5. <strong>Have a Rollback Plan</strong></h3>
<p>Always have a <strong>tested fallback path</strong> (like our S3 ingestion) to recover quickly in case of failure.</p>

<hr />

<h2 id="final-thoughts">Final Thoughts</h2>

<p>We’re still awaiting AWS’s official root cause analysis. But we were lucky: Our fallback pipelines kept this from becoming a full-blown production disaster.</p>

<p>I hope this post helps others avoid the trap we fell into.</p>

<p><strong>– Siddharth</strong></p>]]></content><author><name>Siddharth Jain</name></author><category term="database" /><summary type="html"><![CDATA[Our vector database of choice is the OpenSearch service. We initially used AWS’s serverless instance to power our Retrieval-Augmented Generation (RAG) applications. However, slow ingestion speeds led us to migrate to a managed cluster setup.]]></summary></entry><entry><title type="html">Setting up a Jellyfin server with Tailscale on Raspberry Pi</title><link href="https://blog.tellsiddh.com/media/raspberry-pi/tailscale/jellyfin/jellyfin-tailscale-rpi-setup/" rel="alternate" type="text/html" title="Setting up a Jellyfin server with Tailscale on Raspberry Pi" /><published>2025-07-04T00:00:00+00:00</published><updated>2025-07-04T00:00:00+00:00</updated><id>https://blog.tellsiddh.com/media/raspberry-pi/tailscale/jellyfin/jellyfin-tailscale-rpi-setup</id><content type="html" xml:base="https://blog.tellsiddh.com/media/raspberry-pi/tailscale/jellyfin/jellyfin-tailscale-rpi-setup/"><![CDATA[<p>In this post, I will guide you through the process of setting up a Jellyfin media server on a Raspberry Pi and accessing it securely using Tailscale. This setup allows you to stream your media content from anywhere while ensuring that your connection is secure and private.</p>

<!--more-->

<h2 id="prerequisites">Prerequisites</h2>
<p>Before we begin, ensure you have the following:</p>
<ul>
  <li>A Raspberry Pi (preferably Raspberry Pi 4 or later)</li>
  <li>A microSD card with Raspberry Pi OS installed</li>
  <li>Basic knowledge of using the terminal</li>
  <li>An internet connection for your Raspberry Pi</li>
</ul>

<h2 id="step-1-update-your-raspberry-pi">Step 1: Update Your Raspberry Pi</h2>
<p>First, make sure your Raspberry Pi is up to date. Open a terminal and run the following commands:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt update
<span class="nb">sudo </span>apt upgrade <span class="nt">-y</span>
</code></pre></div></div>

<p>I installed the Ubuntu server version on my Raspberry Pi, but you can use any Raspberry Pi OS variant. I selected this version because I wanted a minimal setup without a desktop environment I can access via SSH.</p>

<h2 id="step-2-install-jellyfin">Step 2: Install Jellyfin</h2>
<p>Next, we will install Jellyfin. Run the following commands in your terminal:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>curl https://repo.jellyfin.org/install-debuntu.sh | <span class="nb">sudo </span>bash
<span class="nb">sudo </span>ufw allow 8096/tcp
</code></pre></div></div>

<p>The below links provide additional information on installing Jellyfin. They are discussion threads that can help you troubleshoot any issues you might encounter during the installation process.
You can also find the official installation guide on the Jellyfin website.</p>

<p>https://github.com/jellyfin/jellyfin/discussions/7460</p>

<p>https://gist.github.com/aslafy-z/dce9fd98bbe42f21095eb231687ae4f5</p>

<p>That’s it! Jellyfin is now installed on your Raspberry Pi. You can access the Jellyfin web interface by navigating to <code class="language-plaintext highlighter-rouge">http://&lt;your-raspberry-pi-ip&gt;:8096</code> in your web browser.</p>

<p>Add your jellyfin user in your user group to allow all access needed:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>usermod <span class="nt">-aG</span> <span class="nv">$USER</span> jellyfin
</code></pre></div></div>

<p>Commands to check status and restart Jellyfin:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl status jellyfin
<span class="nb">sudo </span>systemctl restart jellyfin
</code></pre></div></div>

<p>Make sure your content folder has the correct permissions:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">chmod </span>o+x /home/raspberrypi/Content
</code></pre></div></div>

<h2 id="step-3-install-tailscale">Step 3: Install Tailscale</h2>
<p>Now, we will install Tailscale to securely access your Jellyfin server from anywhere. Run the following commands:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-fsSL</span> https://tailscale.com/install.sh | sh
<span class="nb">sudo </span>tailscale up
</code></pre></div></div>

<p>To check the status of Tailscale, you can use:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tailscale status
</code></pre></div></div>

<p>Once you login to Tailscale, you will need to download the tailscale client on your devices (laptop, phone, etc.) to access the Jellyfin server. You can find the Tailscale client for various platforms on their <a href="https://tailscale.com/download">official website</a>.</p>

<h2 id="step-4-accessing-jellyfin-via-tailscale">Step 4: Accessing Jellyfin via Tailscale</h2>
<p>After installing Tailscale on your devices, you can access your Jellyfin server by navigating to <code class="language-plaintext highlighter-rouge">http://&lt;tailscale-ip&gt;:8096</code> in your web browser. The Tailscale IP can be found in the Tailscale admin console or by running <code class="language-plaintext highlighter-rouge">tailscale status</code> on your Raspberry Pi.</p>

<p>You can also use the local IP address of your Raspberry Pi if you want. It can be accessed locally and via Tailscale. The local IP address can be found by running:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">hostname</span> <span class="nt">-I</span>
</code></pre></div></div>

<h2 id="note">Note</h2>

<p>On my iPhone, I added the Tailscale client and was able to access the Jellyfin server without any issues. The streaming quality was excellent, and I could browse my media library seamlessly. I had to configure the VPN but it was straightforward. I also set up the Jellyfin app on my iPhone, which allowed me to stream content directly from the app without needing to use a web browser. I use FinAmp, a Jellyfin client for iOS, which provides a great user experience for streaming audio.</p>

<h2 id="conclusion">Conclusion</h2>
<p>You have successfully set up a Jellyfin media server on your Raspberry Pi and secured it with Tailscale. Now you can enjoy your media content from anywhere, securely and privately.</p>

<p><strong>– Siddharth</strong></p>]]></content><author><name>Siddharth Jain</name></author><category term="media" /><category term="raspberry-pi" /><category term="tailscale" /><category term="jellyfin" /><summary type="html"><![CDATA[In this post, I will guide you through the process of setting up a Jellyfin media server on a Raspberry Pi and accessing it securely using Tailscale. This setup allows you to stream your media content from anywhere while ensuring that your connection is secure and private.]]></summary></entry><entry><title type="html">Hello, World!</title><link href="https://blog.tellsiddh.com/personal/hello-world/" rel="alternate" type="text/html" title="Hello, World!" /><published>2025-05-26T00:00:00+00:00</published><updated>2025-05-26T00:00:00+00:00</updated><id>https://blog.tellsiddh.com/personal/hello-world</id><content type="html" xml:base="https://blog.tellsiddh.com/personal/hello-world/"><![CDATA[<p>Welcome to my blog!<br />
I’m <strong>Siddharth</strong>, a software engineer who loves to build, experiment, and learn new things. I’ve created this blog as a way to document my journey, share what I learn, and hopefully help others who are on a similar path.</p>

<hr />

<h2 id="what-youll-find-here">What You’ll Find Here</h2>

<p>This blog is my digital notebook. I’ll be writing about things I’m working on, learning, or thinking about. Topics will include:</p>

<ul>
  <li><strong>Thoughts on tech and engineering</strong> – insights, opinions, or breakdowns of tools and processes I find useful</li>
  <li><strong>Beginner-friendly tutorials</strong> – step-by-step guides for solving real problems (the kind I wish I found when I was learning!)</li>
  <li><strong>Personal stories</strong> – my experiences in tech, career milestones, mistakes made, and lessons learned</li>
  <li><strong>Ideas and experiments</strong> – side projects, coding challenges, or just “what if?” scenarios I’m tinkering with</li>
</ul>

<p>Whether you’re new to programming or a seasoned developer, I hope there’s something here for you.</p>

<hr />

<h2 id="why-i-chose-jekyll">Why I Chose Jekyll</h2>

<p>When starting a blog, I had a choice: use a platform like Medium or WordPress, or build something myself. I chose <a href="https://jekyllrb.com/"><strong>Jekyll</strong></a>, a static site generator, because:</p>

<ul>
  <li><strong>Speed</strong> – Jekyll builds a static website, meaning it’s fast and doesn’t need a database</li>
  <li><strong>Simplicity</strong> – I write posts using Markdown (plain text with formatting), which is clean and distraction-free</li>
  <li><strong>Customizable</strong> – I can tweak the layout, design, and features just the way I want</li>
  <li><strong>Fun to tinker with</strong> – I enjoy diving into the internals and learning how things work</li>
</ul>

<p>To make things look good and reduce setup time, I’m using the <a href="https://mmistakes.github.io/minimal-mistakes/">Minimal Mistakes</a> theme – a beautiful and flexible theme designed for Jekyll blogs.</p>

<hr />

<h2 id="running-jekyll-locally">Running Jekyll Locally</h2>

<p>Before publishing your blog online, it’s a good idea to see what it looks like on your own computer. Here’s how I run my Jekyll blog locally:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle <span class="nb">exec </span>jekyll server
</code></pre></div></div>

<p>This command tells Jekyll to start a local web server. You can then open a browser and go to <code class="language-plaintext highlighter-rouge">http://localhost:4000</code> to see your blog as it will appear when live.</p>

<p><em>Tip:</em> You need to have Ruby, Bundler, and Jekyll installed for this to work. Don’t worry – I’ll write a step-by-step guide soon!</p>

<hr />

<h2 id="my-jekyll-configuration-explained">My Jekyll Configuration (Explained)</h2>

<p>Here’s a peek at my <code class="language-plaintext highlighter-rouge">_config.yml</code> file – the brain of the blog setup. Each line controls part of how the blog behaves:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">title</span><span class="pi">:</span> <span class="s">tellsiddh's blog</span>                <span class="c1"># The blog title shown in the browser</span>
<span class="na">description</span><span class="pi">:</span> <span class="s">A simple blog powered by Jekyll</span>
<span class="na">url</span><span class="pi">:</span> <span class="s2">"</span><span class="s">https://blog.tellsiddh.com"</span>     <span class="c1"># The actual URL of your site</span>
<span class="na">baseurl</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span>                            <span class="c1"># Used if your blog is in a subfolder (leave blank for root)</span>

<span class="na">permalink</span><span class="pi">:</span> <span class="s">/:categories/:title/</span>       <span class="c1"># Controls the URL format (e.g. /personal/hello-world/)</span>

<span class="na">remote_theme</span><span class="pi">:</span> <span class="s2">"</span><span class="s">mmistakes/minimal-mistakes@4.27.1"</span>   <span class="c1"># The theme used</span>
<span class="na">minimal_mistakes_skin</span><span class="pi">:</span> <span class="s2">"</span><span class="s">dark"</span>         <span class="c1"># Theme style (others: default, air, neon...)</span>

<span class="na">excerpt_separator</span><span class="pi">:</span> <span class="s2">"</span><span class="s">&lt;!--more--&gt;"</span>      <span class="c1"># Marks where a summary ends and the full post continues</span>

<span class="na">plugins</span><span class="pi">:</span>                              <span class="c1"># Adds extra features to the blog</span>
  <span class="pi">-</span> <span class="s">jekyll-feed</span>                       <span class="c1"># Generates an RSS feed</span>
  <span class="pi">-</span> <span class="s">jekyll-sitemap</span>                    <span class="c1"># Adds a sitemap for better SEO</span>
  <span class="pi">-</span> <span class="s">jekyll-seo-tag</span>                    <span class="c1"># Helps improve search engine visibility</span>
  <span class="pi">-</span> <span class="s">jekyll-include-cache</span>              <span class="c1"># Optimizes page includes for speed</span>

<span class="na">author</span><span class="pi">:</span> <span class="s">siddharth</span>                      <span class="c1"># My author name for posts</span>

<span class="na">category_archive</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">liquid</span>
  <span class="na">path</span><span class="pi">:</span> <span class="s">/categories/</span>                  <span class="c1"># Enables viewing posts by category</span>

<span class="na">tag_archive</span><span class="pi">:</span>
  <span class="na">type</span><span class="pi">:</span> <span class="s">liquid</span>
  <span class="na">path</span><span class="pi">:</span> <span class="s">/tags/</span>                        <span class="c1"># Enables viewing posts by tag</span>

<span class="na">twitter_username</span><span class="pi">:</span> <span class="s">tellsiddh</span>           <span class="c1"># Links to my Twitter</span>
<span class="na">github_username</span><span class="pi">:</span> <span class="s">tellsiddh</span>            <span class="c1"># Links to my GitHub</span>
</code></pre></div></div>

<p><em>Don’t worry if this looks complicated – once you get the hang of it, customizing your blog becomes second nature.</em></p>

<hr />

<h2 id="whats-next">What’s Next?</h2>

<p>Now that the blog is live, here’s what you can expect from future posts:</p>

<ul>
  <li>In-depth guides on tools like Git, VS Code, Jekyll, and more</li>
  <li>Breakdowns of side projects I’m building</li>
  <li>Thoughts on working in tech, growing as a developer, and building a career</li>
  <li>Possibly some random nerdy stuff I just couldn’t resist writing about</li>
</ul>

<hr />

<p>If you’ve read this far, thank you
I’m truly excited to share this space with you.</p>

<p>Want to connect or follow along?</p>

<p><a href="https://github.com/tellsiddh">GitHub</a><br />
<a href="https://twitter.com/tellsiddh">Twitter</a></p>

<p>Stay curious,<br />
<strong>Siddharth</strong></p>]]></content><author><name>Siddharth Jain</name></author><category term="personal" /><summary type="html"><![CDATA[Welcome to my blog! I’m Siddharth, a software engineer who loves to build, experiment, and learn new things. I’ve created this blog as a way to document my journey, share what I learn, and hopefully help others who are on a similar path. What You’ll Find Here This blog is my digital notebook. I’ll be writing about things I’m working on, learning, or thinking about. Topics will include: Thoughts on tech and engineering – insights, opinions, or breakdowns of tools and processes I find useful Beginner-friendly tutorials – step-by-step guides for solving real problems (the kind I wish I found when I was learning!) Personal stories – my experiences in tech, career milestones, mistakes made, and lessons learned Ideas and experiments – side projects, coding challenges, or just “what if?” scenarios I’m tinkering with Whether you’re new to programming or a seasoned developer, I hope there’s something here for you. Why I Chose Jekyll When starting a blog, I had a choice: use a platform like Medium or WordPress, or build something myself. I chose Jekyll, a static site generator, because: Speed – Jekyll builds a static website, meaning it’s fast and doesn’t need a database Simplicity – I write posts using Markdown (plain text with formatting), which is clean and distraction-free Customizable – I can tweak the layout, design, and features just the way I want Fun to tinker with – I enjoy diving into the internals and learning how things work To make things look good and reduce setup time, I’m using the Minimal Mistakes theme – a beautiful and flexible theme designed for Jekyll blogs. Running Jekyll Locally Before publishing your blog online, it’s a good idea to see what it looks like on your own computer. Here’s how I run my Jekyll blog locally: bundle exec jekyll server This command tells Jekyll to start a local web server. You can then open a browser and go to http://localhost:4000 to see your blog as it will appear when live. Tip: You need to have Ruby, Bundler, and Jekyll installed for this to work. Don’t worry – I’ll write a step-by-step guide soon! My Jekyll Configuration (Explained) Here’s a peek at my _config.yml file – the brain of the blog setup. Each line controls part of how the blog behaves: ```yaml title: tellsiddh’s blog # The blog title shown in the browser description: A simple blog powered by Jekyll url: “https://blog.tellsiddh.com” # The actual URL of your site baseurl: “” # Used if your blog is in a subfolder (leave blank for root) permalink: /:categories/:title/ # Controls the URL format (e.g. /personal/hello-world/) remote_theme: “mmistakes/minimal-mistakes@4.27.1” # The theme used minimal_mistakes_skin: “dark” # Theme style (others: default, air, neon…) excerpt_separator: “]]></summary></entry><entry><title type="html">My Terminal Workflow</title><link href="https://blog.tellsiddh.com/tech/terminal-workflow/" rel="alternate" type="text/html" title="My Terminal Workflow" /><published>2025-05-26T00:00:00+00:00</published><updated>2025-05-26T00:00:00+00:00</updated><id>https://blog.tellsiddh.com/tech/terminal-workflow</id><content type="html" xml:base="https://blog.tellsiddh.com/tech/terminal-workflow/"><![CDATA[<p>I’ve collected and used thousands of terminal commands through actual devops, AI/ML, data science, full-stack web development, and cloud engineering workflows.</p>

<p>This post captures <strong>the most frequently used</strong>, <strong>most impactful</strong>, and <strong>uniquely powerful</strong> commands from that experience. Each one has been battle-tested and integrated into real engineering environments.</p>

<!--more-->

<h2 id="setup--shell-enhancements">Setup &amp; Shell Enhancements</h2>

<h3 id="switch-to-the-fish-shell-friendly-interactive-shell">Switch to the <code class="language-plaintext highlighter-rouge">fish</code> shell (Friendly Interactive Shell)</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>fish
which fish
<span class="nb">sudo </span>sh <span class="nt">-c</span> <span class="s1">'echo /opt/homebrew/bin/fish &gt;&gt; /etc/shells'</span>
chsh <span class="nt">-s</span> /opt/homebrew/bin/fish
fish
</code></pre></div></div>

<blockquote>
  <p>Enables better auto-suggestions, syntax highlighting, and user-friendliness.</p>
</blockquote>

<hr />

<h3 id="install-core-tools">Install Core Tools</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>git htop jq wget tree curl tmux
</code></pre></div></div>

<blockquote>
  <p>These are essentials for terminal power users.</p>
</blockquote>

<hr />

<h3 id="add-paths-in-fish-shell">Add PATHs in <code class="language-plaintext highlighter-rouge">fish</code> shell</h3>

<pre><code class="language-fish">fish_add_path /opt/homebrew/bin/
</code></pre>

<p>Make it persistent via <code class="language-plaintext highlighter-rouge">config.fish</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano ~/.config/fish/config.fish
</code></pre></div></div>

<hr />

<h3 id="use-alias-for-shortcuts">Use <code class="language-plaintext highlighter-rouge">alias</code> for shortcuts</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">alias </span><span class="nv">gs</span><span class="o">=</span><span class="s2">"git status"</span>
<span class="nb">alias </span><span class="nv">ll</span><span class="o">=</span><span class="s2">"ls -lah"</span>
<span class="nb">alias </span><span class="nv">gc</span><span class="o">=</span><span class="s2">"git commit -m"</span>
<span class="nb">alias </span><span class="nv">gco</span><span class="o">=</span><span class="s2">"git checkout"</span>
</code></pre></div></div>

<p>Save to:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>nano ~/.config/fish/config.fish
</code></pre></div></div>

<hr />

<h2 id="git-daily-driver-for-projects">Git: Daily Driver for Projects</h2>

<p>These are the most-used git commands that show up repeatedly across dev workflows:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone git@github.com:user/project.git
git checkout <span class="nt">-b</span> feature/my-branch
git switch branch-name
git commit <span class="nt">-m</span> <span class="s2">"meaningful message"</span>
git push <span class="nt">--set-upstream</span> origin branch-name
git stash / git stash pop
git reset HEAD~1
git log <span class="nt">--oneline</span> <span class="nt">--graph</span> <span class="nt">--all</span>
</code></pre></div></div>

<p>Create and jump to a new feature branch:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout <span class="nt">-b</span> fix/critical-issue
</code></pre></div></div>

<p>View status or staged diff:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git status
git diff
</code></pre></div></div>

<hr />

<h2 id="python-development-workflows">Python Development Workflows</h2>

<p>Create isolated environments with <code class="language-plaintext highlighter-rouge">conda</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>conda create <span class="nt">-n</span> myenv <span class="nv">python</span><span class="o">=</span>3.10
conda activate myenv
conda <span class="nb">install </span>numpy pandas jupyter
</code></pre></div></div>

<p>Run scripts:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python my_script.py
python <span class="nt">-m</span> http.server
</code></pre></div></div>

<p>Profile performance:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">-m</span> cProfile <span class="nt">-s</span> cumulative script.py
</code></pre></div></div>

<p>Use <code class="language-plaintext highlighter-rouge">tuna</code> to visualize import times:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install </span>tuna
python <span class="nt">-X</span> importtime script.py 2&gt; import.log
tuna import.log
</code></pre></div></div>

<hr />

<h2 id="networking-ssh--system-tools">Networking, SSH &amp; System Tools</h2>

<h3 id="ssh-with-custom-keys">SSH with custom keys</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh <span class="nt">-i</span> ~/.ssh/id_rsa user@host
</code></pre></div></div>

<p>Copy your key:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh-copy-id user@host
</code></pre></div></div>

<hr />

<h3 id="diagnose-dns-ip-connectivity">Diagnose DNS, IP, Connectivity</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ping google.com
dig example.com
nslookup example.com
ifconfig | <span class="nb">grep </span>broadcast
</code></pre></div></div>

<p>Find your local IP:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ipconfig getifaddr en0
</code></pre></div></div>

<hr />

<h3 id="scan-network-devices">Scan network devices</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nmap <span class="nt">-sn</span> 192.168.1.0/24
</code></pre></div></div>

<hr />

<h2 id="package-managers">Package Managers</h2>

<h3 id="brew-macos">Brew (macOS)</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew <span class="nb">install </span>wget git jq
brew update
brew upgrade
</code></pre></div></div>

<hr />

<h3 id="pip--python-packages">Pip &amp; Python packages</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install </span>package-name
pip uninstall package-name
pip freeze <span class="o">&gt;</span> requirements.txt
pip <span class="nb">install</span> <span class="nt">-r</span> requirements.txt
</code></pre></div></div>

<hr />

<h2 id="file-and-text-utilities">File and Text Utilities</h2>

<h3 id="search-history">Search history</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">history</span> | <span class="nb">grep</span> <span class="s2">"ssh"</span>
</code></pre></div></div>

<h3 id="grep-for-content-in-files">Grep for content in files</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">grep</span> <span class="nt">-r</span> <span class="s2">"keyword"</span> <span class="nb">.</span>
<span class="nb">grep</span> <span class="s2">"error"</span> logs.txt
</code></pre></div></div>

<hr />

<h3 id="recursive-file-ops">Recursive file ops</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find <span class="nb">.</span> <span class="nt">-name</span> <span class="s2">"*.py"</span>
find <span class="nb">.</span> <span class="nt">-type</span> f <span class="nt">-exec</span> <span class="nb">cat</span> <span class="o">{}</span> +
</code></pre></div></div>

<hr />

<h3 id="disk-space">Disk space</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">df</span> <span class="nt">-h</span>
<span class="nb">du</span> <span class="nt">-sh</span> <span class="k">*</span>
</code></pre></div></div>

<hr />

<h3 id="clean-terminal">Clean terminal</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>clear
Ctrl + L
</code></pre></div></div>

<hr />

<h2 id="system-monitoring">System Monitoring</h2>

<h3 id="htop">htop</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nb">install </span>htop
htop
</code></pre></div></div>

<blockquote>
  <p>Better than <code class="language-plaintext highlighter-rouge">top</code> — shows processes, memory, swap, threads, and more.</p>
</blockquote>

<hr />

<h3 id="check-ports">Check ports</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lsof <span class="nt">-i</span> :8000
<span class="nb">sudo </span>lsof <span class="nt">-i</span> <span class="nt">-P</span> | <span class="nb">grep</span> <span class="s1">':8080'</span>
</code></pre></div></div>

<p>Kill process by PID:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">kill</span> <span class="nt">-9</span> &lt;PID&gt;
</code></pre></div></div>

<hr />

<h2 id="automation-scripts">Automation Scripts</h2>

<p>Make a Python file executable:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">chmod</span> +x script.py
./script.py
</code></pre></div></div>

<p>Zip files:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>zip <span class="nt">-r</span> archive.zip folder/
</code></pre></div></div>

<p>Unzip:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>unzip file.zip
</code></pre></div></div>

<hr />

<h2 id="apis--web-tools">APIs &amp; Web Tools</h2>

<h3 id="curl-with-headers">Curl with headers</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-X</span> POST https://api.example.com/endpoint <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{"key":"value"}'</span>
</code></pre></div></div>

<hr />

<h3 id="secure-file-uploads-example">Secure file uploads (example)</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-X</span> PUT <span class="s2">"</span><span class="nv">$SIGNED_S3_URL</span><span class="s2">"</span> <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/pdf"</span> <span class="se">\</span>
  <span class="nt">--upload-file</span> ./file.pdf
</code></pre></div></div>

<hr />

<h2 id="audio-tools-ai--speech">Audio Tools (AI &amp; Speech)</h2>

<h3 id="tts-using-llama-tts">TTS using LLaMA-TTS:</h3>

<p>This needs llama.cpp setup, binary works.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>llama-tts <span class="nt">--tts-out-default</span> <span class="nt">-p</span> <span class="s2">"Hello world"</span> <span class="o">&amp;&amp;</span> ffplay output.wav <span class="nt">-nodisp</span> <span class="nt">-autoexit</span>
</code></pre></div></div>

<p>Stream via Flask API:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-X</span> POST http://localhost:5000/tts <span class="se">\</span>
  <span class="nt">-H</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
  <span class="nt">-d</span> <span class="s1">'{"text": "Welcome to the demo"}'</span> <span class="se">\</span>
  <span class="nt">--output</span> output.wav
</code></pre></div></div>

<hr />

<h2 id="data-engineering-tools">Data Engineering Tools</h2>

<h3 id="terraform">Terraform</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>terraform init
terraform plan <span class="nt">-out</span><span class="o">=</span>tfplan
terraform apply tfplan
terraform destroy
</code></pre></div></div>

<p>Target specific modules:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>terraform apply <span class="nt">-target</span><span class="o">=</span>module.my_module
</code></pre></div></div>

<hr />

<h3 id="aws-cli">AWS CLI</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws s3 <span class="nb">ls
</span>aws configure
aws lambda invoke <span class="nt">--function-name</span> my-func out.txt
</code></pre></div></div>

<p>Use localstack:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">LOCALSTACK_AUTH_TOKEN</span><span class="o">=</span>dummy localstack start
</code></pre></div></div>

<hr />

<h2 id="high-impact-one-liners">High-impact One-liners</h2>

<h3 id="re-run-last-command-with-sudo">Re-run last command with <code class="language-plaintext highlighter-rouge">sudo</code></h3>

<p>!! does not work on fish, not sure why?</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo</span> <span class="o">!!</span>
</code></pre></div></div>

<h3 id="repeat-previous-command">Repeat previous command</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">!!</span>
</code></pre></div></div>

<h3 id="view-last-100-commands">View last 100 commands</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">history</span> | <span class="nb">tail</span> <span class="nt">-n</span> 100
</code></pre></div></div>

<hr />

<h3 id="git-reset--clean">Git reset &amp; clean</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git reset <span class="nt">--hard</span> HEAD
git clean <span class="nt">-fd</span>
</code></pre></div></div>

<hr />

<h2 id="miscellaneous-yet-powerful">Miscellaneous Yet Powerful</h2>

<h3 id="view-markdown-docs-in-browser">View markdown docs in browser</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 <span class="nt">-m</span> http.server 8000
</code></pre></div></div>

<h3 id="set-environment-variables">Set environment variables</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">AWS_PROFILE</span><span class="o">=</span>default
<span class="nb">export </span><span class="nv">OPENAI_API_KEY</span><span class="o">=</span>your-key
</code></pre></div></div>

<h3 id="decode-jwt">Decode JWT</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s2">"&lt;JWT_PART_2&gt;"</span> | <span class="nb">base64</span> <span class="nt">--decode</span>
</code></pre></div></div>

<hr />

<h2 id="final-words">Final Words</h2>

<p>These commands reflect months of actual engineering activity — not just tutorials. If you found this useful:</p>

<ul>
  <li>Follow <a href="https://twitter.com/tellsiddh">@tellsiddh</a></li>
  <li>Star <a href="https://github.com/tellsiddh">my GitHub</a></li>
  <li>Check out <a href="https://blog.tellsiddh.com">blog.tellsiddh.com</a></li>
</ul>

<p>Stay curious and keep building!<br />
— <strong>Siddharth</strong></p>]]></content><author><name>Siddharth Jain</name></author><category term="tech" /><summary type="html"><![CDATA[I’ve collected and used thousands of terminal commands through actual devops, AI/ML, data science, full-stack web development, and cloud engineering workflows. This post captures the most frequently used, most impactful, and uniquely powerful commands from that experience. Each one has been battle-tested and integrated into real engineering environments.]]></summary></entry></feed>