<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Harun R. Rayhan</title>
    <description>The latest articles on DEV Community by Harun R. Rayhan (@harundotdev).</description>
    <link>https://dev.to/harundotdev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F396534%2Ff7bd70b8-4228-41af-a500-4a98417a9670.jpg</url>
      <title>DEV Community: Harun R. Rayhan</title>
      <link>https://dev.to/harundotdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/harundotdev"/>
    <language>en</language>
    <item>
      <title>How I Review Terraform and Lambda PRs with AI Before They Merge</title>
      <dc:creator>Harun R. Rayhan</dc:creator>
      <pubDate>Wed, 14 Oct 2026 14:57:46 +0000</pubDate>
      <link>https://dev.to/harundotdev/how-i-review-terraform-and-lambda-prs-with-ai-before-they-merge-3j4a</link>
      <guid>https://dev.to/harundotdev/how-i-review-terraform-and-lambda-prs-with-ai-before-they-merge-3j4a</guid>
      <description>&lt;p&gt;If my earlier posts were about how I write infrastructure and how I build AI-assisted workflows, this one is about the gate right before merge.&lt;/p&gt;

&lt;p&gt;That distinction matters. Writing code with AI is useful. Reviewing production changes with AI is where the real value shows up for me. The problem is not getting a first draft fast. The problem is making sure the change is still safe when it touches IAM, Terraform state, Lambda runtime settings, or anything that can quietly cost money or break production.&lt;/p&gt;

&lt;p&gt;So I started using AI as a first-pass reviewer for Terraform and Lambda pull requests. Not as the decision maker. Not as the person who approves the merge. Just as the reviewer that helps me catch the obvious and semi-obvious issues before I spend my own time on the full manual pass.&lt;/p&gt;

&lt;p&gt;That keeps the review fast, but it also keeps the final responsibility where it belongs, with me.&lt;/p&gt;

&lt;p&gt;This post is the workflow I trust today.&lt;/p&gt;

&lt;p&gt;Short version: AI reviews the diff, I review the risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Is a Different Post From My Other AI and AWS Articles
&lt;/h2&gt;

&lt;p&gt;I already write about &lt;a href="https://harun.dev/blog/how-i-make-ai-coding-agents-safe-in-a-real-aws-codebase" rel="noopener noreferrer"&gt;AI in AWS codebases&lt;/a&gt;, &lt;a href="https://harun.dev/blog/claude-code-for-aws-infrastructure-agentic-devops-workflow-with-terraform" rel="noopener noreferrer"&gt;Terraform workflows&lt;/a&gt;, and &lt;a href="https://harun.dev/blog/multi-agent-claude-code-aws-devops-routines" rel="noopener noreferrer"&gt;agentic DevOps routines&lt;/a&gt;. This post is not another version of that same story.&lt;/p&gt;

&lt;p&gt;The earlier posts focus on one of two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how I use AI to help build infrastructure&lt;/li&gt;
&lt;li&gt;how I structure the workflow around the codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This one is narrower. It is about reviewing a production change before it merges.&lt;/p&gt;

&lt;p&gt;That narrower scope makes it more useful in practice, because the questions are more specific:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did this IAM policy get broader?&lt;/li&gt;
&lt;li&gt;Did someone accidentally make a Lambda slower, hotter, or more expensive?&lt;/li&gt;
&lt;li&gt;Did Terraform replace something that should not be replaced?&lt;/li&gt;
&lt;li&gt;Did we open a networking path we did not mean to open?&lt;/li&gt;
&lt;li&gt;Did the PR forget tests, docs, or rollback notes?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the kind of review AI is actually good at helping with.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Feed the Reviewer
&lt;/h2&gt;

&lt;p&gt;I do not ask the model to guess from thin air. I give it the actual change surface.&lt;/p&gt;

&lt;p&gt;Typically that means some mix of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the pull request diff&lt;/li&gt;
&lt;li&gt;the Terraform plan output&lt;/li&gt;
&lt;li&gt;the files touched by the change&lt;/li&gt;
&lt;li&gt;the environment or service context&lt;/li&gt;
&lt;li&gt;any recent deployment or incident notes that matter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The quality of the review depends on the input. If I give it a sloppy prompt, I get a sloppy review. If I give it the real plan and the real diff, it can usually spot the things I want it to spot.&lt;/p&gt;

&lt;p&gt;One caution: redact before you paste. Plan output can include values that are not marked sensitive, so I strip anything I would not put in a public issue.&lt;/p&gt;

&lt;p&gt;I want the reviewer to answer a simple question: what would make this change risky to merge?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Checks I Care About Most
&lt;/h2&gt;

&lt;p&gt;For Terraform and Lambda work, I care about a small set of risk categories more than anything else.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. IAM and permissions
&lt;/h3&gt;

&lt;p&gt;This is the first thing I look at because permission creep is easy to miss in a diff. A policy can go from narrow to broad with one extra wildcard or one extra resource pattern.&lt;/p&gt;

&lt;p&gt;I want AI to flag:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;new wildcards in actions or resources&lt;/li&gt;
&lt;li&gt;roles that gained access to more services than before&lt;/li&gt;
&lt;li&gt;policies that look temporary but are actually permanent&lt;/li&gt;
&lt;li&gt;anything that could grant access across environments&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Lambda runtime and cost impact
&lt;/h3&gt;

&lt;p&gt;Lambda changes can look tiny and still affect cost or reliability.&lt;/p&gt;

&lt;p&gt;I want the reviewer to notice things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory jumps that are probably unnecessary&lt;/li&gt;
&lt;li&gt;timeouts that are too short or too generous&lt;/li&gt;
&lt;li&gt;concurrency changes that might cause throttle or burst issues&lt;/li&gt;
&lt;li&gt;extra cold-start risk from dependencies or packaging changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Terraform replacement risk
&lt;/h3&gt;

&lt;p&gt;This is where production changes can become unpleasant. A harmless-looking refactor can trigger a destroy-and-recreate path if someone changes the wrong attribute.&lt;/p&gt;

&lt;p&gt;I want the model to call out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;resources marked for replacement&lt;/li&gt;
&lt;li&gt;stateful resources with a destructive path&lt;/li&gt;
&lt;li&gt;name changes that can force recreation&lt;/li&gt;
&lt;li&gt;security groups, targets, or buckets that could disappear briefly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Networking and exposure
&lt;/h3&gt;

&lt;p&gt;Anything that changes public access, private access, VPC wiring, or routing deserves extra attention.&lt;/p&gt;

&lt;p&gt;I want the reviewer to be loud about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;public exposure that was not there before&lt;/li&gt;
&lt;li&gt;new ingress or egress paths&lt;/li&gt;
&lt;li&gt;load balancer or API Gateway routing changes&lt;/li&gt;
&lt;li&gt;subnet or security group edits with hidden blast radius&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Missing operational detail
&lt;/h3&gt;

&lt;p&gt;Even if the code is correct, the review is not complete if the PR forgot the operational side.&lt;/p&gt;

&lt;p&gt;So I want AI to ask about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rollback plan&lt;/li&gt;
&lt;li&gt;testing evidence&lt;/li&gt;
&lt;li&gt;migration order&lt;/li&gt;
&lt;li&gt;monitoring and alert impact&lt;/li&gt;
&lt;li&gt;what happens if this fails halfway through&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I Still Review Manually
&lt;/h2&gt;

&lt;p&gt;AI helps me move faster, but some things should stay human-led.&lt;/p&gt;

&lt;p&gt;My manual pass focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the actual blast radius&lt;/li&gt;
&lt;li&gt;whether the change matches product intent&lt;/li&gt;
&lt;li&gt;whether rollback is realistic&lt;/li&gt;
&lt;li&gt;what stateful resources are involved&lt;/li&gt;
&lt;li&gt;whether the diff makes sense in the larger release plan&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also sanity-check anything that looks too confident. If the model says a change is safe, that is not enough. I want the evidence behind the claim.&lt;/p&gt;

&lt;p&gt;The best outcome is not “AI approved it.” The best outcome is “AI surfaced the right questions before I reviewed it.”&lt;/p&gt;

&lt;h2&gt;
  
  
  My Review Flow
&lt;/h2&gt;

&lt;p&gt;The flow is pretty simple.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Collect the PR diff and Terraform plan.&lt;/li&gt;
&lt;li&gt;Ask the AI reviewer to summarize the change in plain English.&lt;/li&gt;
&lt;li&gt;Ask for the top risks, sorted by severity.&lt;/li&gt;
&lt;li&gt;Ask for anything that looks inconsistent or missing.&lt;/li&gt;
&lt;li&gt;Do my own manual pass on the flagged areas.&lt;/li&gt;
&lt;li&gt;Approve, request changes, or split the PR if the risk is too broad.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That sounds basic, but it works because it keeps the review structured.&lt;/p&gt;

&lt;p&gt;Here is the kind of prompt I use conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are reviewing a production AWS change.

Focus on:
- IAM and permissions
- Terraform replacement risk
- Lambda runtime and cost impact
- networking and exposure
- missing rollback or operational detail

Return:
1. plain-English summary of the change
2. top risks ranked by severity
3. specific lines or resources that look suspicious
4. questions I should answer before merge
5. a merge recommendation: safe, caution, or block
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I do not need poetry from the reviewer. I need a compact risk report I can act on.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Good Review Finds the Questions, Not Just the Bugs
&lt;/h2&gt;

&lt;p&gt;Sometimes the most useful thing the AI does is not catch a mistake outright. It points out that something deserves a question.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why did this Lambda need double the memory?&lt;/li&gt;
&lt;li&gt;Why is this IAM role suddenly allowed to touch more than one environment?&lt;/li&gt;
&lt;li&gt;Why does this Terraform change replace the resource instead of updating it in place?&lt;/li&gt;
&lt;li&gt;Why is this public endpoint now reachable from a wider range of traffic?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That kind of review is valuable because it slows me down in the right place. It makes the risky bit visible before I merge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Complements My Other AI Workflow Posts
&lt;/h2&gt;

&lt;p&gt;If I compare this to my other posts, the shape is different.&lt;/p&gt;

&lt;p&gt;Earlier posts are about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;using AI to build faster&lt;/li&gt;
&lt;li&gt;breaking work into specialists&lt;/li&gt;
&lt;li&gt;keeping agents safe in production codebases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This one is about the final gate before the change goes live.&lt;/p&gt;

&lt;p&gt;That is why I think it is a good companion piece instead of a duplicate. It belongs in the same family, but it answers a different question.&lt;/p&gt;

&lt;p&gt;One post is about the tools that help me work. This post is about the review layer that keeps me honest.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Want the Reviewer To Avoid
&lt;/h2&gt;

&lt;p&gt;There are a few failure modes I do not want from an AI reviewer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do not rubber-stamp the diff.&lt;/li&gt;
&lt;li&gt;Do not hide uncertainty behind confident language.&lt;/li&gt;
&lt;li&gt;Do not recommend merge without explaining why.&lt;/li&gt;
&lt;li&gt;Do not ignore infrastructure changes just because the application code looks small.&lt;/li&gt;
&lt;li&gt;Do not treat production Terraform like toy code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the model cannot explain the risk, it is not helping much.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;AI review works best when it makes the important things easier to see, not when it replaces judgment.&lt;/p&gt;

&lt;p&gt;That is the real pattern I keep coming back to in AWS work. I want speed, but I want it inside a review process that still respects the cost of being wrong.&lt;/p&gt;

&lt;p&gt;So yes, I use AI to review Terraform and Lambda PRs. But the point is not to automate approval. The point is to catch the risky stuff earlier, ask better questions, and make the human review sharper.&lt;/p&gt;

&lt;p&gt;That is the version I trust in production.&lt;/p&gt;




&lt;p&gt;Originally published at &lt;a href="https://harun.dev/blog/production-ai-code-review-for-terraform-and-lambda-prs" rel="noopener noreferrer"&gt;harun.dev&lt;/a&gt;. The full interactive version lives there. Follow me on &lt;a href="https://x.com/harundotdev" rel="noopener noreferrer"&gt;X&lt;/a&gt; or read my &lt;a href="https://harun.dev/bio" rel="noopener noreferrer"&gt;bio&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>devops</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Building a Production RAG Pipeline on AWS Lambda and pgvector</title>
      <dc:creator>Harun R. Rayhan</dc:creator>
      <pubDate>Tue, 13 Oct 2026 13:36:41 +0000</pubDate>
      <link>https://dev.to/harundotdev/building-a-production-rag-pipeline-on-aws-lambda-and-pgvector-11d2</link>
      <guid>https://dev.to/harundotdev/building-a-production-rag-pipeline-on-aws-lambda-and-pgvector-11d2</guid>
      <description>&lt;p&gt;I was knee-deep in building my SaaS when users started asking questions I couldn't answer from the UI alone. My app had a solid backend, Lambda functions, Fastify, PostgreSQL on RDS. I wrote about that whole architecture in my previous post. But users wanted to search through documentation, find answers buried in help articles, and ask natural-language questions about the product. The UI just wasn't built for that.&lt;/p&gt;

&lt;p&gt;So I started looking into RAG. Every tutorial I found showed the same setup: LangChain running locally, Pinecone for vectors, and a Python script tying it all together. Great for demos. Not great for a production SaaS already running on AWS.&lt;/p&gt;

&lt;p&gt;Here's the thing. I didn't want to add another service to my stack. I was already paying for PostgreSQL on RDS. I was already managing Lambda functions. Adding Pinecone or Weaviate meant another bill, another SDK, another thing to monitor at 2 AM. Then I found pgvector, and everything clicked.&lt;/p&gt;

&lt;p&gt;pgvector is a PostgreSQL extension. It adds vector storage and similarity search right inside the database I was already running. No new infrastructure. No new vendor. Just an extension. I paired it with Lambda for compute, Bedrock for the LLM, and RDS Proxy for connection pooling. The whole pipeline costs me about $54/month at 10K queries per day.&lt;/p&gt;

&lt;p&gt;Let me walk you through every piece of how I built it.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.pexels.com/@tara-winstead" rel="noopener noreferrer"&gt;Tara Winstead&lt;/a&gt; on &lt;a href="https://www.pexels.com/photo/an-artificial-intelligence-illustration-on-the-wall-8849295/" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is RAG and Why Does It Work
&lt;/h2&gt;

&lt;p&gt;RAG stands for Retrieval-Augmented Generation. The name sounds academic, but the idea is simple. And once I understood it, I realized it was exactly what my users were asking for without knowing the term.&lt;/p&gt;

&lt;p&gt;You have your own data. Documents, help articles, product specs, whatever. You want an LLM to answer questions about that data. But the LLM was trained on the internet, not your private stuff. So you give it context at query time.&lt;/p&gt;

&lt;p&gt;The flow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;User asks a question.&lt;/strong&gt; "How do I reset my API key?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval step.&lt;/strong&gt; You search your own documents for the most relevant chunks. This is semantic search, not keyword matching. You're comparing the meaning of the question against the meaning of each chunk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generation step.&lt;/strong&gt; You stuff those relevant chunks into a prompt and send it to the LLM. "Based on the following context, answer this question."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM responds&lt;/strong&gt; with an answer grounded in your actual data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. No fine-tuning. No retraining. No waiting weeks for a model to learn your docs.&lt;/p&gt;

&lt;p&gt;And that's exactly why RAG beats fine-tuning for most use cases. Fine-tuning is slow, expensive, and your model becomes stale the moment your data changes. With RAG, you just update the documents in your database. The next query picks up the new content automatically. Your data is always live.&lt;/p&gt;

&lt;p&gt;The magic is in the retrieval step. If you retrieve the right chunks, the LLM gives great answers. If you retrieve garbage, the LLM confidently presents garbage. So the vector search layer matters a lot. That's where pgvector comes in. And for my SaaS, it meant I could keep everything inside the PostgreSQL instance I was already running.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.pexels.com/@cottonbro" rel="noopener noreferrer"&gt;cottonbro studio&lt;/a&gt; on &lt;a href="https://www.pexels.com/photo/google-browser-on-laptop-6986455/" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up pgvector in PostgreSQL (RDS)
&lt;/h2&gt;

&lt;p&gt;When I first looked at Pinecone, I nearly added it. The docs are slick and the onboarding is smooth. Then I checked the pricing page and realized I was already paying for PostgreSQL. Why add another database when I could just enable an extension?&lt;/p&gt;

&lt;p&gt;If you're already running PostgreSQL on RDS, adding vector search is surprisingly easy. You don't need Pinecone. You don't need Weaviate. You just enable an extension.&lt;/p&gt;

&lt;p&gt;pgvector adds a &lt;code&gt;vector&lt;/code&gt; column type to PostgreSQL. You store embeddings as vectors, then query them with distance operators. Cosine similarity, L2 distance, inner product. All the standard stuff.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enable the Extension
&lt;/h3&gt;

&lt;p&gt;First, make sure your RDS parameter group allows pgvector. Here's the CloudFormation snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;RDSParameterGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::RDS::DBParameterGroup&lt;/span&gt;
  &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Family&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres16&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;PostgreSQL&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;16&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pgvector"&lt;/span&gt;
    &lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;shared_preload_libraries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_stat_statements,pgvector"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run the SQL to create the extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. PostgreSQL now understands vectors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the Embeddings Table
&lt;/h3&gt;

&lt;p&gt;Here's the schema I use. The &lt;code&gt;vector(1536)&lt;/code&gt; column size matches OpenAI's &lt;code&gt;text-embedding-3-small&lt;/code&gt; model. If you're using Bedrock Titan, switch to &lt;code&gt;vector(1024)&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;hrr_document_chunks&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;            &lt;span class="n"&gt;BIGSERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;document_id&lt;/span&gt;   &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;chunk_index&lt;/span&gt;   &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;content&lt;/span&gt;       &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;metadata&lt;/span&gt;      &lt;span class="n"&gt;JSONB&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'{}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;embedding&lt;/span&gt;     &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1536&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt;    &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="n"&gt;updated_at&lt;/span&gt;    &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;

  &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk_index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;metadata&lt;/code&gt; column is a JSONB field where I store things like the source URL, document title, section heading, and any other context that helps with filtering later. You'll want this when your dataset grows and you need to scope searches to specific document types.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create an HNSW Index
&lt;/h3&gt;

&lt;p&gt;Without an index, pgvector does a sequential scan. That's fine for a few thousand rows. But once you hit tens of thousands of embeddings, you need an index.&lt;/p&gt;

&lt;p&gt;I recommend HNSW over IVFFlat. HNSW is slightly slower to build but gives better recall and works much better for Lambda workloads. More on that in the lessons learned section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_chunks_embedding_hnsw&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;hrr_document_chunks&lt;/span&gt;
  &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;hnsw&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;vector_cosine_ops&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ef_construction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;m&lt;/code&gt; and &lt;code&gt;ef_construction&lt;/code&gt; parameters control the index quality. Higher values give better recall but slower inserts. These defaults work well for datasets up to about 500K embeddings. I landed on these numbers after a week of testing different configurations against my own dataset.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz2vdupepljoa0sqhqctu.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz2vdupepljoa0sqhqctu.jpeg" alt="PostgreSQL database setup and schema" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.pexels.com/@rahulp9800" rel="noopener noreferrer"&gt;Rahul Pandit&lt;/a&gt; on &lt;a href="https://www.pexels.com/photo/blue-and-red-light-from-computer-1933900/" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating and Storing Embeddings
&lt;/h2&gt;

&lt;p&gt;Now you need to get your data into that table. This is the ingestion pipeline: take your documents, break them into chunks, generate embeddings for each chunk, and store everything in pgvector.&lt;/p&gt;

&lt;p&gt;For my SaaS, the documents were help articles, product guides, and FAQ pages that users kept asking about. I run ingestion as a separate Lambda function. It gets triggered whenever new documents are added, either through an S3 event or a direct API call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chunking
&lt;/h3&gt;

&lt;p&gt;Chunking strategy matters more than most people think. I split documents into chunks of about 500 tokens with a 50-token overlap between consecutive chunks. The overlap prevents information from getting lost at chunk boundaries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;chunkDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;overlap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sentences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;(?&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;=&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;.!?&lt;/span&gt;&lt;span class="se"&gt;])\s&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentChunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sentence&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sentenceTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;estimateTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentLength&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;sentenceTokens&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;maxTokens&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;currentChunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentChunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

      &lt;span class="c1"&gt;// Keep last few sentences for overlap&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;overlapSentences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;overlapLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentChunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;overlapLength&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;estimateTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentChunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;overlapLength&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;overlap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;overlapSentences&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentChunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;currentChunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;overlapSentences&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;currentLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;overlapLength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;currentChunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sentence&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;currentLength&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;sentenceTokens&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentChunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentChunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;estimateTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Generating Embeddings with Bedrock Titan
&lt;/h3&gt;

&lt;p&gt;Here's how to call Bedrock's Titan Embeddings model. This uses the AWS SDK v3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BedrockRuntimeClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InvokeModelCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-bedrock-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bedrockClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BedrockRuntimeClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bedrockClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InvokeModelCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;modelId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;amazon.titan-embed-text-v2:0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;inputText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;dimensions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextDecoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you prefer OpenAI embeddings instead, the call is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateEmbeddingOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.openai.com/v1/embeddings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-embedding-3-small&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Upserting Chunks
&lt;/h3&gt;

&lt;p&gt;Now bring it all together. Chunk the document, embed each chunk, and upsert into pgvector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ingestDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chunkDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;generateEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`INSERT INTO hrr_document_chunks (document_id, chunk_index, content, metadata, embedding)
       VALUES ($1, $2, $3, $4, $5::vector)
       ON CONFLICT (document_id, chunk_index)
       DO UPDATE SET content = $3, metadata = $4, embedding = $5::vector, updated_at = NOW()`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Clean up old chunks if document got shorter&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`DELETE FROM hrr_document_chunks WHERE document_id = $1 AND chunk_index &amp;gt;= $2`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ON CONFLICT&lt;/code&gt; clause handles re-ingestion cleanly. If a document gets updated, you just run ingest again and it overwrites the old chunks. The cleanup query at the end removes stale chunks if the document got shorter after an edit.&lt;/p&gt;

&lt;p&gt;One thing to watch out for: Bedrock Titan has a rate limit. I hit this on day one when I tried to ingest my entire knowledge base at once. If you're ingesting thousands of documents, add a small delay between embedding calls or use batch processing. I throttle to about 10 requests per second and that's been reliable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmui7tq4a0knofmtc7nlw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmui7tq4a0knofmtc7nlw.jpeg" alt="Data processing and ingestion pipeline" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.pexels.com/@dungmedia" rel="noopener noreferrer"&gt;Media Dung&lt;/a&gt; on &lt;a href="https://www.pexels.com/photo/computer-and-scanner-in-laboratory-17940257/" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieval: Semantic Search on Lambda
&lt;/h2&gt;

&lt;p&gt;This is the part that made it all feel real. A user sends a question, and you need to find the most relevant chunks from your database. Fast.&lt;/p&gt;

&lt;p&gt;The first time I ran a semantic search against my own docs and got back exactly the right paragraph, I knew this was going to work. The retrieval Lambda takes a query string, embeds it using the same model you used for ingestion, then runs a cosine similarity search against pgvector. The whole thing takes about 100-200ms on a warm Lambda invocation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;retrieveChunks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;topK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Embed the user's query with the same model used for ingestion&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryEmbedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;generateEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Cosine similarity search using pgvector's &amp;lt;=&amp;gt; operator&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`SELECT
       id,
       document_id,
       content,
       metadata,
       1 - (embedding &amp;lt;=&amp;gt; $1::vector) AS similarity
     FROM hrr_document_chunks
     WHERE 1 - (embedding &amp;lt;=&amp;gt; $1::vector) &amp;gt; 0.3
     ORDER BY embedding &amp;lt;=&amp;gt; $1::vector
     LIMIT $2`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;queryEmbedding&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;topK&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things to notice here.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;=&amp;gt;&lt;/code&gt; operator is pgvector's cosine distance. Lower distance means higher similarity. I compute &lt;code&gt;1 - distance&lt;/code&gt; to get a similarity score between 0 and 1. The &lt;code&gt;WHERE&lt;/code&gt; clause filters out anything below 0.3 similarity, which cuts out the noise. You don't want the LLM seeing irrelevant chunks.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;topK&lt;/code&gt; parameter controls how many chunks you return. I default to 5. Going higher gives the LLM more context but costs more tokens. Going lower risks missing relevant information. Five is a good starting point for most use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connection Pooling with RDS Proxy
&lt;/h3&gt;

&lt;p&gt;If you read my previous post, you know this pattern. Lambda functions can spin up hundreds of concurrent instances. Each one opens a database connection. Without connection pooling, you'll exhaust PostgreSQL's connection limit in seconds.&lt;/p&gt;

&lt;p&gt;RDS Proxy handles this. Your Lambda points to the proxy endpoint, and the proxy multiplexes connections to the actual database. The code is identical to a normal &lt;code&gt;pg.Pool&lt;/code&gt; setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pg&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// RDS Proxy endpoint&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5432&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;idleTimeoutMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;connectionTimeoutMillis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;rejectUnauthorized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;max: 5&lt;/code&gt; on the Lambda side. The proxy handles the actual pooling across all Lambda instances. Keeping the per-function pool small prevents any single Lambda from hogging connections.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ctht43i8a6u4dt1ls5f.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ctht43i8a6u4dt1ls5f.jpeg" alt="Semantic search and vector similarity" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.pexels.com/@n-voitkevich" rel="noopener noreferrer"&gt;Nataliya Vaitkevich&lt;/a&gt; on &lt;a href="https://www.pexels.com/photo/smartphone-on-black-table-6120185/" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generation: Calling the LLM with Context
&lt;/h2&gt;

&lt;p&gt;You've retrieved the relevant chunks. Now you need to turn them into an answer. This is where the LLM comes in.&lt;/p&gt;

&lt;p&gt;I went back and forth on this part. The idea is simple: build a prompt that includes the retrieved chunks as context, along with the user's question. Then call the LLM and return whatever it generates. But getting the prompt right took more iterations than I expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Prompt Template
&lt;/h3&gt;

&lt;p&gt;Here's the prompt template I use. It's deliberately strict about staying grounded in the context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`[Source &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`You are a helpful assistant that answers questions based on the provided context.
Rules:
- Only use information from the provided context to answer.
- If the context does not contain enough information, say so honestly.
- Keep answers concise and direct.
- Reference which source(s) you used when possible.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Context:
&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;

Question: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;

Answer based only on the context above.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That system prompt is important. I learned this after my first demo went sideways. Without explicit instructions to stay grounded, the LLM will happily make things up using its training data. One of my test users asked about a feature we didn't have, and the LLM invented a whole tutorial for it. The "say so honestly" part is key. I'd rather get an "I don't have enough information" response than a confident hallucination.&lt;/p&gt;

&lt;h3&gt;
  
  
  Calling Bedrock Claude
&lt;/h3&gt;

&lt;p&gt;Here's the Bedrock call using Claude Haiku. I use Haiku instead of Sonnet for RAG because it's 10x cheaper and the quality difference is minimal when you're giving it good context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BedrockRuntimeClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InvokeModelCommand&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@aws-sdk/client-bedrock-runtime&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bedrockClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BedrockRuntimeClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;us-east-1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateAnswer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;buildPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;bedrockClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InvokeModelCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;modelId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;anthropic.claude-3-haiku-20240307-v1:0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;anthropic_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bedrock-2023-05-31&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;system&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;system&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextDecoder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling Token Limits
&lt;/h3&gt;

&lt;p&gt;Here's a practical concern. Each chunk might be 500 tokens. If you retrieve 5 chunks, that's 2,500 tokens of context. Add the system prompt, the question, and the response, and you're looking at maybe 4,000 tokens total. That fits easily within Haiku's context window.&lt;/p&gt;

&lt;p&gt;But what if your chunks are longer, or you need more of them? I cap the total context at 3,000 tokens and truncate the lowest-similarity chunks first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;truncateContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;totalTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;estimateTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalTokens&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;maxTokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;totalTokens&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;selected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the chunks are already sorted by similarity (highest first), this naturally keeps the most relevant ones and drops the least relevant. Simple and effective.&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.pexels.com/@bertellifotografia" rel="noopener noreferrer"&gt;Matheus Bertelli&lt;/a&gt; on &lt;a href="https://www.pexels.com/photo/ai-chat-interface-display-on-laptop-30530406/" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring It Together: The API Endpoint
&lt;/h2&gt;

&lt;p&gt;Now let's connect everything. This is where my existing SaaS architecture paid off. The API endpoint receives a question, runs retrieval, generates an answer, and returns it. I use Fastify on Lambda (same setup as my previous post), and adding a new route was straightforward. The pattern works with any framework.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ragHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;question&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Question is required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 1: Retrieve relevant chunks&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;retrieveChunks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I couldn't find any relevant information to answer your question.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 2: Truncate context to fit token limits&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trimmedChunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;truncateContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 3: Generate answer&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;generateAnswer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;trimmedChunks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Step 4: Return answer with source metadata&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;trimmedChunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;document_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;similarity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;similarity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="na"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Register the route&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/rag/ask&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;question&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maxLength&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;preHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ragHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The handler is intentionally thin. Retrieval and generation are separate modules. This makes testing easy. You can unit test the chunking, the retrieval query, and the prompt template independently. The handler just orchestrates the flow.&lt;/p&gt;

&lt;p&gt;I also return the source metadata alongside the answer. This is useful for two reasons. First, your frontend can show users where the answer came from, which builds trust. Second, it helps you debug retrieval quality. If the answer is wrong, check the sources. Nine times out of ten, the problem is that retrieval returned the wrong chunks, not that the LLM misunderstood them.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;preHandler&lt;/code&gt; uses the same authentication middleware from my existing API. This was one of the perks of building RAG into my existing stack instead of bolting on a separate service. Same auth, same rate limiting, same logging. RAG endpoints should always be authenticated. You don't want someone querying your private documents without authorization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgf3ynsaiu2f30glvfla6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgf3ynsaiu2f30glvfla6.jpeg" alt="API endpoint and server architecture" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.pexels.com/@brett-sayles" rel="noopener noreferrer"&gt;Brett Sayles&lt;/a&gt; on &lt;a href="https://www.pexels.com/photo/server-racks-on-data-center-5480781/" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost Breakdown
&lt;/h2&gt;

&lt;p&gt;Let's talk real numbers. This was a big deal for me because I didn't want RAG to double my AWS bill. I'm going to break down costs for a small AI SaaS handling about 10,000 RAG queries per day.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Infrastructure
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Monthly Cost&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RDS db.t3.micro (PostgreSQL + pgvector)&lt;/td&gt;
&lt;td&gt;~$15&lt;/td&gt;
&lt;td&gt;2 vCPU, 1GB RAM, 20GB storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS Proxy&lt;/td&gt;
&lt;td&gt;~$20&lt;/td&gt;
&lt;td&gt;Connection pooling for Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda (retrieval + generation)&lt;/td&gt;
&lt;td&gt;~$5&lt;/td&gt;
&lt;td&gt;512MB, avg 300ms per invocation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Gateway (HTTP API)&lt;/td&gt;
&lt;td&gt;~$3&lt;/td&gt;
&lt;td&gt;$1 per million requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bedrock Titan Embeddings&lt;/td&gt;
&lt;td&gt;~$3&lt;/td&gt;
&lt;td&gt;~$0.10 per 1M tokens, ~30M tokens/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bedrock Claude Haiku (generation)&lt;/td&gt;
&lt;td&gt;~$8&lt;/td&gt;
&lt;td&gt;~$0.25/1M input, ~$1.25/1M output tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$54/month&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's $54/month for a production RAG pipeline handling 10K queries per day. Not bad.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compared to Hosted Vector Databases
&lt;/h3&gt;

&lt;p&gt;Now let's compare to what you'd pay with a dedicated vector database:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pinecone Starter&lt;/strong&gt;: ~$70/month for 1M vectors. And you still need the LLM costs on top of that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weaviate Cloud&lt;/strong&gt;: ~$25/month for their starter tier. Plus compute for the actual search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qdrant Cloud&lt;/strong&gt;: ~$30/month for a small instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With pgvector, the vector storage cost is zero because it's part of your existing PostgreSQL instance. You're already paying for RDS. The extension is free. This is exactly why I went this route. I just added vector search to a database I was already running and paying for.&lt;/p&gt;

&lt;p&gt;The math is clear: if you already have PostgreSQL on RDS, pgvector is the cheapest vector database alternative out there. You avoid a whole separate service, a separate bill, and separate operational overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where the Money Actually Goes
&lt;/h3&gt;

&lt;p&gt;The biggest cost in a RAG pipeline is not storage or compute. It's the LLM generation calls. At 10K queries per day with Claude Haiku, you're spending about $8/month on generation alone. Switch to Claude Sonnet and that jumps to ~$80/month. Switch to GPT-4o and you're looking at ~$150/month.&lt;/p&gt;

&lt;p&gt;So choose your generation model carefully. For most RAG use cases, Haiku or GPT-4o-mini gives you 90% of the quality at 10% of the cost. Save the big models for cases where answer quality justifies the spend.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fde0e3tmgmuxps6k4whcv.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fde0e3tmgmuxps6k4whcv.jpeg" alt="Cost analysis and comparison chart" width="799" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.pexels.com/@leeloothefirst" rel="noopener noreferrer"&gt;Leeloo The First&lt;/a&gt; on &lt;a href="https://www.pexels.com/photo/magnifying-glass-on-white-paper-with-statistical-data-5561913/" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;After running this RAG pipeline in production for a few months, here are the things I learned the hard way. Every one of these cost me hours of debugging or unnecessary spending.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Chunking Strategy Matters More Than the Embedding Model
&lt;/h3&gt;

&lt;p&gt;I wasted about two weeks on this one. I was obsessing over Titan vs. OpenAI embeddings, running benchmarks, comparing similarity scores. The difference in retrieval quality between them was maybe 5%. Then I changed my chunking from fixed 1000-token chunks to 500-token chunks with overlap, and retrieval quality jumped by 30%. Two weeks of model comparison, undone by a single config change.&lt;/p&gt;

&lt;p&gt;The reason is obvious in hindsight. Smaller chunks are more precise. When a user asks a specific question, a 500-token chunk about that exact topic scores much higher than a 1000-token chunk where the relevant part is buried in the middle. The overlap makes sure you don't lose context at chunk boundaries.&lt;/p&gt;

&lt;p&gt;If your RAG answers feel "off," look at your chunks before you look at your embedding model. I wish someone had told me that from day one.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. HNSW Index vs. IVFFlat for Lambda
&lt;/h3&gt;

&lt;p&gt;This one bit me in production. pgvector supports two index types: IVFFlat and HNSW. I started with IVFFlat because the internet says it's faster to build. That was a mistake I didn't catch until users complained about slow first responses.&lt;/p&gt;

&lt;p&gt;IVFFlat requires a "warm-up" query to load the index into memory. On Lambda, that warm-up happens on every cold start. It added about 400ms to the first query after a cold start. My users noticed. I got support tickets about it.&lt;/p&gt;

&lt;p&gt;HNSW doesn't need warm-up. The first query is about the same speed as subsequent ones. The moment I switched, the cold start complaints stopped. For Lambda workloads where cold starts are a reality, HNSW is the better choice. The trade-off is that HNSW uses more memory and takes longer to build, but you're building the index once and querying it thousands of times. The build cost is irrelevant.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Cache Embeddings for Repeated Queries
&lt;/h3&gt;

&lt;p&gt;I noticed this pattern about a month in. Users ask the same questions over and over. "How do I reset my password?" "What are the pricing tiers?" "How do I cancel?" I was paying Bedrock to embed identical strings dozens of times a day.&lt;/p&gt;

&lt;p&gt;Embedding the same question every time wastes money and adds latency. I added a simple cache using a hash of the query text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embeddingCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cacheKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embeddingCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;embeddingCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;generateEmbedding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;embeddingCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Keep cache from growing forever&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embeddingCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;embeddingCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;embeddingCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an in-memory cache, so it lives as long as the Lambda container. For a warmer setup, you could use DynamoDB or ElastiCache, but the in-memory approach works surprisingly well. In my case, about 40% of queries are cache hits within the same Lambda container.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Monitor Retrieval Quality Separately from Generation Quality
&lt;/h3&gt;

&lt;p&gt;I spent an entire weekend debugging a "wrong answer" that a user reported. I was blaming the LLM, tweaking the prompt, trying different temperature settings. Then I finally looked at the retrieved chunks and realized the retrieval step had pulled in completely irrelevant paragraphs. The LLM was doing its best with garbage context. That weekend taught me to always check retrieval first.&lt;/p&gt;

&lt;p&gt;Now I log both steps separately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval quality&lt;/strong&gt;: log the query, the returned chunks, and their similarity scores. Review the lowest-scoring returned chunks regularly. If they're irrelevant, your similarity threshold is too low.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generation quality&lt;/strong&gt;: log the prompt (with context) and the LLM's response. When users report bad answers, check whether the context contained the right information.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of the time, bad answers come from bad retrieval. The LLM is usually fine if you give it the right context. So investing in better chunking, better metadata filtering, and tuning your similarity threshold pays off way more than switching to a fancier LLM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fng7xbo09t86ehadbutio.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fng7xbo09t86ehadbutio.jpeg" alt="Lessons learned and knowledge growth" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.pexels.com/@mikhail-nilov" rel="noopener noreferrer"&gt;Mikhail Nilov&lt;/a&gt; on &lt;a href="https://www.pexels.com/photo/back-view-of-a-girl-writing-on-the-whiteboard-8923526/" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;My SaaS now has a &lt;code&gt;/rag/ask&lt;/code&gt; endpoint that my users actually love. They type a question, get an answer grounded in our own documentation, and see exactly which sources the answer came from. The whole thing runs on the same Lambda and PostgreSQL stack I was already using. No new services. No new vendors. Just pgvector and a few hundred lines of code.&lt;/p&gt;

&lt;p&gt;Looking back, the decision to keep everything inside my existing stack was the right call. I avoided a separate vector database bill, a separate SDK to maintain, and a separate service to monitor. pgvector gave me semantic search, HNSW indexing, and cosine similarity queries inside the database I already knew how to operate. The total cost sits around $54/month at 10K queries per day.&lt;/p&gt;

&lt;p&gt;If I were starting over, I'd change one thing. I would spend more time on chunking from the very beginning instead of comparing embedding models. Good chunks make everything downstream better. Bad chunks make everything worse, no matter how fancy your LLM is. I learned that lesson the expensive way.&lt;/p&gt;

&lt;p&gt;For anyone building an AI SaaS on AWS, this setup hits a sweet spot. Low cost, minimal operational overhead, and your data stays in PostgreSQL where you can query it with regular SQL alongside your vector searches. If you already have the Lambda and RDS stack running, you're closer to production RAG than you think.&lt;/p&gt;

&lt;p&gt;Now go build something cool with it.&lt;/p&gt;

&lt;p&gt;Hope you enjoyed this walkthrough. If you have questions about any part of the pipeline, or if you've built something similar and want to compare notes, reach out.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me on &lt;a href="https://x.com/harundotdev" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt; for more posts about AWS, serverless, and building AI products. I share what actually works in production, not just what looks good in tutorials.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.pexels.com/@nicolas-foster-65973708" rel="noopener noreferrer"&gt;Nicolas Foster&lt;/a&gt; on &lt;a href="https://www.pexels.com/photo/vintage-amd-motherboard-with-pci-and-agp-slots-34920288/" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://harun.dev/blog/building-a-production-rag-pipeline-on-aws-lambda-pgvector" rel="noopener noreferrer"&gt;harun.dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>rag</category>
      <category>postgres</category>
    </item>
    <item>
      <title>First 3 things I always do after creating a new AWS account</title>
      <dc:creator>Harun R. Rayhan</dc:creator>
      <pubDate>Wed, 06 Jul 2022 14:24:50 +0000</pubDate>
      <link>https://dev.to/aws-builders/first-3-things-i-always-do-after-creating-a-new-aws-account-4cap</link>
      <guid>https://dev.to/aws-builders/first-3-things-i-always-do-after-creating-a-new-aws-account-4cap</guid>
      <description>&lt;p&gt;As part of my job, I create AWS accounts frequently. I do lots of things depending on account needs before starting to use it. I found 3 common things I do with all of the new AWS accounts. I made a checklist. Nowadays I do these 3 things first and then everything else. &lt;/p&gt;

&lt;p&gt;Let's make your account more secure and more protected than ever. 🔐&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Budget with Alerts
&lt;/h2&gt;

&lt;p&gt;One of the common issues with AWS is bills. People gets surprising bills because of misconfiguration or keep expensive services running unknowingly. Although, budget and alerts can NOT fix these issues but help keep bills tidy. And gives alerts if crosses the budget almost instantly.&lt;/p&gt;

&lt;p&gt;Let's set a budget and a couple of alerts. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Go to &lt;a href="https://console.aws.amazon.com/billing/home#/budgets" rel="noopener noreferrer"&gt;&lt;strong&gt;Budget Console&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click on the "&lt;strong&gt;Create Budget&lt;/strong&gt;" button.&lt;/li&gt;
&lt;li&gt;Choose the first option, "&lt;strong&gt;Cost budget - Recommended&lt;/strong&gt;" from the next page.&lt;/li&gt;
&lt;li&gt;From the next page: 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs5kzmvidliq7ir6o2j6t.png" alt="CleanShot 2022-07-04 at 20.33.25@2x.png" width="800" height="976"&gt;

&lt;ul&gt;
&lt;li&gt;A. Give it a name.&lt;/li&gt;
&lt;li&gt;B. Set the Period as &lt;strong&gt;Month&lt;/strong&gt; for this demo, but you can choose any other period.&lt;/li&gt;
&lt;li&gt;C. Set Budget renewal type as &lt;strong&gt;Recurring budget&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;D. Choose &lt;strong&gt;Fixed&lt;/strong&gt; from the Budgeting method.&lt;/li&gt;
&lt;li&gt;E. Give a real budget amount to your need, mine is a demo account so $10 is fine for me. Let me iterate again, choose a real amount.&lt;/li&gt;
&lt;li&gt;F. From the bottom-right of the page click "Next"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;From the next page, click on the "&lt;strong&gt;Add an alert threshold&lt;/strong&gt;. Going to set a couple of alerts. 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjpn94j6d2e8igks77a18.png" alt="CleanShot 2022-07-04 at 20.43.58@2x.png" width="800" height="383"&gt;
&lt;/li&gt;
&lt;li&gt;Let's set an actual budget alert: 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftd4utjdee0g62d8rhcqt.png" alt="CleanShot 2022-07-04 at 20.48.00@2x.png" width="800" height="503"&gt;

&lt;ul&gt;
&lt;li&gt;A. Choose &lt;strong&gt;80&lt;/strong&gt; from the threshold.&lt;/li&gt;
&lt;li&gt;B. Select &lt;strong&gt;Actual&lt;/strong&gt; from the trigger&lt;/li&gt;
&lt;li&gt;C. Put as many real and active emails as necessary. (⚠️ please add active email to save yourself from disaster).&lt;/li&gt;
&lt;li&gt;D. Let's add another one.
&lt;em&gt;Actual budget alert only sends notifications when you spent threshold amount. For example, it will send an email to me after spending $8 (80% of 10$). So, we need something else together with it.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now set a forecasted budget alert:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuv7pvcs3hekzp9vdzs9b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuv7pvcs3hekzp9vdzs9b.png" alt="CleanShot 2022-07-04 at 20.55.35@2xa.png" width="800" height="418"&gt;&lt;/a&gt;&lt;br&gt;
Everything is the same as the first alert, only change Trigger to &lt;strong&gt;Forecasted&lt;/strong&gt;. So, it will send you an alert if AWS forecast that you will spend more than your threshold.   After configuring it, click the &lt;strong&gt;Next&lt;/strong&gt; button from the bottom of the page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click the &lt;strong&gt;Next&lt;/strong&gt; again from the next page. And then click the &lt;strong&gt;Create Budget&lt;/strong&gt; button from the final page.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And you are done with, you probably going to be saved from a disaster by email notifications.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Add Multi-Factor Authentication (MFA)
&lt;/h2&gt;

&lt;p&gt;You should use MFA for all IAM accounts including the root account. If not possible, at least apply it to root and privileged accounts. Login to the account (root or IAM) and let's protect it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://console.aws.amazon.com/iam/home#/security_credentials$mfa" rel="noopener noreferrer"&gt;&lt;strong&gt;Security Credentials of IAM Console&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click on the "Assign MFA device" 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1s4jlusrmeclzi2rpeqf.png" alt="CleanShot 2022-07-06 at 18.45.13@2x.png" width="800" height="502"&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Virtual MFA device&lt;/strong&gt; from the popup&lt;/li&gt;
&lt;li&gt;You can choose either of these apps: Authy, Duo Mobile, LastPass Authenticator, Microsoft Authenticator, Google Authenticator, Symantec VIP. My personal choice is Authy.&lt;/li&gt;
&lt;li&gt;Click on the "Show QR code" from the next popup and scan it from your MFA app. Then enter two codes (one after another). And then click the "Assign MFA" button. 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3ixiw7jny8qbpebrzzgq.png" alt="CleanShot 2022-07-06 at 18.52.42@2x.png" width="800" height="816"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;h2&gt;
  
  
  (bonus) Set Alias for the Account
&lt;/h2&gt;

&lt;p&gt;Account ID or Alias is required to log in IAM account. Account ID is a long number. Humans are bad with a long number. A meaningful alias can save you from the trouble of remembering the account ID. Let's set an alias for your account: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://console.aws.amazon.com/iamv2" rel="noopener noreferrer"&gt;&lt;strong&gt;IAM Console&lt;/strong&gt;&lt;/a&gt; after login into the root account.&lt;/li&gt;
&lt;li&gt;You will see a &lt;strong&gt;Create&lt;/strong&gt; button in the right sidebar under the account alias. &lt;/li&gt;
&lt;li&gt;Choose a unique and memorable alias for your account and click "Save changes". &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Use the alias instead of account ID next time you are logging into an IAM account.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Create IAM account(s) for Day-to-Day tasks
&lt;/h2&gt;

&lt;p&gt;You need the root account for a few &lt;a href="https://docs.aws.amazon.com/accounts/latest/reference/root-user-tasks.html" rel="noopener noreferrer"&gt;tasks&lt;/a&gt;. Other than that you should not use the Root account. Perhaps, not for day-to-day tasks, lunching, and provisioning services. &lt;/p&gt;

&lt;p&gt;I always create an IAM account with admin permission. I do all of the tasks from this account except those tasks that require the root account. So, let's create an admin IAM account. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="//console.aws.amazon.com/iamv2/home#/users"&gt;&lt;strong&gt;Users page of IAM Console&lt;/strong&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on the &lt;strong&gt;Add users&lt;/strong&gt; button from top-right corner. &lt;/li&gt;
&lt;li&gt;You will be in this page: 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2x7jokqsohnbmeae2ks3.png" alt="CleanShot 2022-07-06 at 19.42.16@2x.png" width="800" height="699"&gt;

&lt;ul&gt;
&lt;li&gt;A. Choose an username&lt;/li&gt;
&lt;li&gt;B. Chosse access type. I need both CLI and AWS Console access. (UPDATE: Don’t choose CLI, if not necessary. You can create different IAM users for CLI actions.)&lt;/li&gt;
&lt;li&gt;C. You can enter your custom password or choose auto generated.&lt;/li&gt;
&lt;li&gt;D. It's better to require reset password to choose it's own.&lt;/li&gt;
&lt;li&gt;E. Click on the &lt;strong&gt;Next: Permission&lt;/strong&gt; button from the bottom-right&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;You are in the permission page: 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjv7yqi6iabvtip6y8f3s.png" alt="CleanShot 2022-07-06 at 19.50.04@2x.png" width="800" height="684"&gt;

&lt;ul&gt;
&lt;li&gt;A. Choose &lt;strong&gt;Attach existing policies directly&lt;/strong&gt; from the top options&lt;/li&gt;
&lt;li&gt;B. Search for the &lt;strong&gt;AdministratorAccess&lt;/strong&gt; role&lt;/li&gt;
&lt;li&gt;C. Choose the &lt;strong&gt;AdministratorAccess&lt;/strong&gt; role&lt;/li&gt;
&lt;li&gt;D. Click on the &lt;strong&gt;Next: Tags&lt;/strong&gt; button&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;You are in the tags page. You can add you tag(s) if you want but it's optional. Click in the &lt;strong&gt;Next: Review&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;Take a look on everything and check if it's matches to mine. And then click on the &lt;strong&gt;Create user&lt;/strong&gt; button. 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6i363czwt3xwewjor4xg.png" alt="CleanShot 2022-07-06 at 19.55.22@2x.png" width="800" height="666"&gt;
&lt;/li&gt;
&lt;li&gt;Now we are in the final page: 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq5ndokqbkt1obp8jwtmk.png" alt="CleanShot 2022-07-06 at 19.57.03@2x.png" width="800" height="371"&gt;

&lt;ul&gt;
&lt;li&gt;A. The link to login to the console&lt;/li&gt;
&lt;li&gt;B &amp;amp; C. Username &amp;amp; Password to login to the console&lt;/li&gt;
&lt;li&gt;D &amp;amp; E. Access key &amp;amp; secret to login via CLI or App&lt;/li&gt;
&lt;li&gt;F. You can download the .csv file with credentails. You can save it in safer place for later use. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;...&lt;/p&gt;

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

&lt;p&gt;There are more things to do to save bills and tighten security of your account. But these are the first 3 things I do and doesn't require much technical knowledge. &lt;/p&gt;

&lt;p&gt;You can subscribe to my newsletter in &lt;a href="https://blog.harun.dev" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;. Follow me on &lt;a href="https://twitter.com/HarunRRayhan" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://dev.to/harunrrayhan"&gt;Dev.to&lt;/a&gt;, and &lt;a href="https://hashnode.com/@HarunRRayhan" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt; for regular updates. Don't hesitate to show your love by clicking Like 👍 button.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Install Cloud Watch Agent in Amazon Lightsail instance for Monitoring, Logging &amp; Debugging</title>
      <dc:creator>Harun R. Rayhan</dc:creator>
      <pubDate>Sat, 04 Jun 2022 04:51:10 +0000</pubDate>
      <link>https://dev.to/aws-builders/install-cloud-watch-agent-in-amazon-lightsail-instance-for-monitoring-logging-debugging-18kj</link>
      <guid>https://dev.to/aws-builders/install-cloud-watch-agent-in-amazon-lightsail-instance-for-monitoring-logging-debugging-18kj</guid>
      <description>&lt;p&gt;Use the Cloud Watch agent to send logging and debugging to Cloud Watch. You can check logs without logging into instances. It's accessible even after the instance is terminated.&lt;/p&gt;

&lt;p&gt;Life is far from perfect, and so does Amazon Lightsail instances. It can be frustrating if there is an error and you don't know how to debug it. The server itself and applications installed in it produces log files. And server stats can be helpful in monitoring, logging, and debugging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;It would help if you had an existing Amazon Lightsail instance. You can create a new instance by following "&lt;a href="https://blog.harun.dev/deploy-wordpress-app-to-amazon-lightsail" rel="noopener noreferrer"&gt;&lt;strong&gt;Deploy WordPress App to Amazon Lightsail&lt;/strong&gt;&lt;/a&gt;" or "&lt;a href="https://blog.harun.dev/deploy-laravel-application-to-amazon-lightsail" rel="noopener noreferrer"&gt;&lt;strong&gt;Deploy Laravel Application to Amazon Lightsail&lt;/strong&gt;&lt;/a&gt;". &lt;/p&gt;

&lt;p&gt;Let's go 🏃‍♂️&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an IAM User
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://console.aws.amazon.com/iam/" rel="noopener noreferrer"&gt;IAM Console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Click on the &lt;strong&gt;Users&lt;/strong&gt; menu from the navigation panel and then click on the &lt;strong&gt;Add Users&lt;/strong&gt; button.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb0xwibvzt0wzqhucephy.png" alt="CleanShot 2022-06-03 at 18.45.52@2x.png" width="800" height="256"&gt;
&lt;/li&gt;
&lt;li&gt;You will be in the &lt;strong&gt;Add user&lt;/strong&gt; screen: 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F82hwpri61tsf31ju6oya.png" alt="CleanShot 2022-06-03 at 18.49.18@2x.png" width="800" height="696"&gt;

&lt;ul&gt;
&lt;li&gt;A. Give it a name. For me, it's &lt;strong&gt;LightsailCloudWatchAgent&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;B. Check the &lt;strong&gt;Access key - Programmatic access&lt;/strong&gt; option&lt;/li&gt;
&lt;li&gt;C. Click on the &lt;strong&gt;Next: Permissions&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;You will be in the &lt;strong&gt;Set permissions&lt;/strong&gt; page:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuqlkpkvgd3yin3tre4g0.png" alt="CleanShot 2022-06-03 at 18.53.25@2x.png" width="800" height="701"&gt;

&lt;ul&gt;
&lt;li&gt;A. Select the &lt;strong&gt;Attach existing policies directly&lt;/strong&gt; option.&lt;/li&gt;
&lt;li&gt;B. Search for &lt;strong&gt;CloudWatchAgentServerPolicy&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;C. Select &lt;strong&gt;CloudWatchAgentServerPolicy&lt;/strong&gt;, and&lt;/li&gt;
&lt;li&gt;D. Click the &lt;strong&gt;Next: Tags&lt;/strong&gt; button.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tags are optional. You can choose your tags. Click on the &lt;strong&gt;Next: Review&lt;/strong&gt; after you are done.&lt;/li&gt;
&lt;li&gt;From review page you can check if everything alright and then click the &lt;strong&gt;Create user&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;Your account is created and you will be in this screen: 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7vwym1l5yglvwhgld0o9.png" alt="CleanShot 2022-06-03 at 19.15.00@2x.png" width="800" height="383"&gt;

&lt;ul&gt;
&lt;li&gt;A. &amp;amp; B. Copy &lt;strong&gt;Access Key ID&lt;/strong&gt; and &lt;strong&gt;Secret access key&lt;/strong&gt; in somewhere safe, or,&lt;/li&gt;
&lt;li&gt;C. Download the csv file.
We will need this access later. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Configure AWS Access
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Check if AWS CLI is installed by running &lt;code&gt;aws --version&lt;/code&gt;. You should see the current version. If you get an error, CLI probably is not installed. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ignore this if AWS CLI is already installed. Run this on ubuntu:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"awscliv2.zip"&lt;/span&gt;
unzip awscliv2.zip
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./aws/install
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./aws/install &lt;span class="nt"&gt;--bin-dir&lt;/span&gt; /usr/bin &lt;span class="nt"&gt;--install-dir&lt;/span&gt; /usr/local/aws-cli &lt;span class="nt"&gt;--update&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow this &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;instruction to install on other servers&lt;/a&gt;. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Now, we are going to configure the AWS access generated in the last step. Run this
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;aws configure &lt;span class="nt"&gt;--profile&lt;/span&gt; AmazonCloudWatchAgent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter Access Key and Secret Like this: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2bi3gu9e846qamj90yls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2bi3gu9e846qamj90yls.png" alt="CleanShot 2022-06-03 at 19.59.18@2x.png" width="799" height="181"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Install the Cloudwatch Agent
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You need to SSH into your instance. Here is &lt;a href="https://blog.harun.dev/deploy-wordpress-app-to-amazon-lightsail#heading-connect-to-the-instance-using-ssh" rel="noopener noreferrer"&gt;how you can SSH into your Amazon Lightsail Instance&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now, let's install the agent. I'm using ubuntu, you can just run this command&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Download the agent&lt;/span&gt;
wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
&lt;span class="c"&gt;# Install the agent&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; ./amazon-cloudwatch-agent.deb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Now run this command configure the agent.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Input your choices as I did. Check the arrow mark. You can choose your own or follow mine.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1654315524093%2FsOZrTU36D.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1654315524093%2FsOZrTU36D.png" alt="CleanShot 2022-06-04 at 10.03.13@2x.png" width="800" height="2154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give AWS Access to the Agent. Run this:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim /opt/aws/amazon-cloudwatch-agent/etc/common-config.toml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Paste this in the bottom of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[credentials]
shared_credential_profile = "AmazonCloudWatchAgent"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now hit &lt;strong&gt;ESC&lt;/strong&gt; and then &lt;strong&gt;:wq!&lt;/strong&gt;. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start the agent:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;amazon-cloudwatch-agent-ctl &lt;span class="nt"&gt;-c&lt;/span&gt; file:/opt/aws/amazon-cloudwatch-agent/bin/config.json &lt;span class="nt"&gt;-a&lt;/span&gt; fetch-config &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check the status of the agent by using:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;amazon-cloudwatch-agent-ctl &lt;span class="nt"&gt;-a&lt;/span&gt; status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something like this: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fodcd8hlcypqfhy5v4xhw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fodcd8hlcypqfhy5v4xhw.png" alt="CleanShot 2022-06-03 at 20.21.53@2x.png" width="799" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify metrics on CloudWatch
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;a href="https://console.aws.amazon.com/cloudwatch" rel="noopener noreferrer"&gt;Cloudwatch Console&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;On the left navigation panel, choose &lt;strong&gt;Metrics&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Under “&lt;strong&gt;Custom Namespaces&lt;/strong&gt;”, You should see a link for “CWAgent”.&lt;/li&gt;
&lt;li&gt;Choose CWAgent.&lt;/li&gt;
&lt;li&gt;Choose any &lt;strong&gt;ImageId, InstanceId, InstanceType&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select the checkbox to display metrics on the graph. Here is mine: 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhe986xrg088und7wx3wj.png" alt="CleanShot 2022-06-04 at 10.08.02@2x.png" width="800" height="374"&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Hope you enjoyed this article. This is the last article of the Amazon Lightsail series.  You can ask me any Lightsail-related question in the comments or on social media.&lt;/p&gt;

&lt;p&gt;You can subscribe to my newsletter to get blog updates every week. Follow me on &lt;a href="https://twitter.com/HarunRRayhan" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://dev.to/harunrrayhan"&gt;Dev.to&lt;/a&gt;, and &lt;a href="https://hashnode.com/@HarunRRayhan" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Connect Redis/Elasticache to application in Amazon Lightsail instance</title>
      <dc:creator>Harun R. Rayhan</dc:creator>
      <pubDate>Sat, 28 May 2022 14:32:18 +0000</pubDate>
      <link>https://dev.to/aws-builders/connect-rediselasticache-to-application-in-amazon-lightsail-instance-4hfb</link>
      <guid>https://dev.to/aws-builders/connect-rediselasticache-to-application-in-amazon-lightsail-instance-4hfb</guid>
      <description>&lt;p&gt;You can create Redis/Elasticache even though Amazon Lightsail doesn't offer it. You can create an Elasticache for Redis cluster in AWS and connect using VPC Peering. &lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;An Amazon Lightsail instance. To create a new instance follow &lt;a href="https://blog.harun.dev/deploy-laravel-application-to-amazon-lightsail" rel="noopener noreferrer"&gt;&lt;strong&gt;Deploy Laravel Application to Amazon Lightsail&lt;/strong&gt;&lt;/a&gt; or &lt;a href="https://blog.harun.dev/deploy-wordpress-app-to-amazon-lightsail" rel="noopener noreferrer"&gt;&lt;strong&gt;Deploy WordPress App to Amazon Lightsail&lt;/strong&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Be a little familiar with AWS Console.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Create Elasticache for Redis
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;a href="https://us-east-1.console.aws.amazon.com/elasticache/home?/dashboard&amp;amp;region=us-east-1#/dashboard?getStarted=expand" rel="noopener noreferrer"&gt;&lt;strong&gt;Elasticache for Redis console&lt;/strong&gt;&lt;/a&gt;. Change the region that you want to launch it. I selected &lt;strong&gt;us-east-1&lt;/strong&gt; as my Lightsail Instance(s) are there. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click the &lt;strong&gt;Create Redis cluster&lt;/strong&gt; button from the center of the page (or wherever you see it)&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frmmxc1tqr6pi8uigi51a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frmmxc1tqr6pi8uigi51a.png" alt="CleanShot 2022-05-28 at 18.24.04@2x.png" width="799" height="414"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You will land in this screen 👇&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1653741612053%2FkUlxh7BaC.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1653741612053%2FkUlxh7BaC.png" alt="CleanShot 2022-05-28 at 18.38.27@2x.png" width="800" height="3092"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A. Choose &lt;strong&gt;Configure and create a new cluster&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;B. Choose cluster mode &lt;strong&gt;Disabled&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;C. Give it a name&lt;/li&gt;
&lt;li&gt;D. Uncheck &lt;strong&gt;Multi-AZ&lt;/strong&gt; Enable&lt;/li&gt;
&lt;li&gt;E. Uncheck &lt;strong&gt;Auto-failover&lt;/strong&gt; Enable&lt;/li&gt;
&lt;li&gt;F. Select a free-tier node type. Currently it's "cache.t3.micro" and "cache.t2.micro".&lt;/li&gt;
&lt;li&gt;G. Do not need any replica for this demo. Make it &lt;strong&gt;"0"&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;H. Give subnet groups a name&lt;/li&gt;
&lt;li&gt;I. Select your VPC&lt;/li&gt;
&lt;li&gt;J. And then clieck the &lt;strong&gt;Next&lt;/strong&gt; button from the bottom.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will be on the second page. Just uncheck &lt;strong&gt;Enable automatic backup&lt;/strong&gt; and click the &lt;strong&gt;Next&lt;/strong&gt; button from the bottom of the page. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmci2m9zuz0hzejha0zbl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmci2m9zuz0hzejha0zbl.png" alt="CleanShot 2022-05-28 at 18.50.07@2x.png" width="800" height="1648"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Review everything on the 3rd and final page. And then click the &lt;strong&gt;Create&lt;/strong&gt; button from the bottom of the page. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It will take a few minutes to get created. Wait for status changes to &lt;strong&gt;Available&lt;/strong&gt;.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2m9yuiex6wju2f81w8i8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2m9yuiex6wju2f81w8i8.png" alt="CleanShot 2022-05-28 at 19.03.41@2x.png" width="799" height="134"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;now, click on the Cluster name. And you will be on the details page. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnn5viz8dfmjz6vd5vm56.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnn5viz8dfmjz6vd5vm56.png" alt="CleanShot 2022-05-28 at 19.05.38@2x.png" width="800" height="369"&gt;&lt;/a&gt;&lt;br&gt;
Copy the &lt;strong&gt;Primary endpoint&lt;/strong&gt; in your clipboard or somewhere safe. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Configure Security Group for Redis cluster
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to the &lt;a href="https://us-east-1.console.aws.amazon.com/vpc/home?region=us-east-1#securityGroups:" rel="noopener noreferrer"&gt;&lt;strong&gt;Security groups&lt;/strong&gt;&lt;/a&gt; of VPS Console. Make sure you are in the same region as the Redis cluster. And then click the &lt;strong&gt;Create security group&lt;/strong&gt; from the top-right corner. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F74rsip3pezi1f8hnoob5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F74rsip3pezi1f8hnoob5.png" alt="CleanShot 2022-05-28 at 19.33.08@2x.png" width="800" height="178"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You are in create Security Group page&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftv8kg1yr81t89wf5mzij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftv8kg1yr81t89wf5mzij.png" alt="CleanShot 2022-05-28 at 19.41.30@2x.png" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A. Give it a name&lt;/li&gt;
&lt;li&gt;B. Click on the &lt;strong&gt;Add rule&lt;/strong&gt; button of the Inbound rules section.&lt;/li&gt;
&lt;li&gt;C. Select 6379 as Custom TCP Port&lt;/li&gt;
&lt;li&gt;D. Add your Amazon Lightsail VPC IP range. To generate it, check your instance's Private IP address. Mine is 172.26.3.157. Replace the last two sections with 0 and add /16. So, mine became 172.26.0.0/16. &lt;/li&gt;
&lt;li&gt;E. Optionally, you can add a description&lt;/li&gt;
&lt;li&gt;F. Then hit the &lt;strong&gt;Create security group&lt;/strong&gt; button from the bottom of the page.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now go to the &lt;strong&gt;Network and security&lt;/strong&gt; tab of your Redis cluster and click the &lt;strong&gt;Modify&lt;/strong&gt; button from the &lt;strong&gt;Security groups&lt;/strong&gt; section.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1653746258674%2FGFFV32uHF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1653746258674%2FGFFV32uHF.png" alt="CleanShot 2022-05-28 at 19.55.02@2x.png" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the &lt;strong&gt;Manage&lt;/strong&gt; button from the next screen&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmko8y6c71a5msunkv2ma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmko8y6c71a5msunkv2ma.png" alt="CleanShot 2022-05-28 at 19.58.02@2x.png" width="800" height="624"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the correct security group from the pop and then click on the &lt;strong&gt;Choose&lt;/strong&gt; button. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fps8l9b4szmrnt7iuihxm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fps8l9b4szmrnt7iuihxm.png" alt="CleanShot 2022-05-28 at 19.59.19@2x.png" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;Modify&lt;/strong&gt; button from the bottom of the page.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fatnwr6143stmpgigmzps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fatnwr6143stmpgigmzps.png" alt="CleanShot 2022-05-28 at 20.00.47@2x.png" width="800" height="845"&gt;&lt;/a&gt;&lt;br&gt;
It will take a few minutes to complete the modification. Wait until the status changes to &lt;strong&gt;Available&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  VPC Peering
&lt;/h2&gt;

&lt;p&gt;The important part of this tutorial. We need to peer VPC between Lightsail and Redis cluster's VPC to connect it. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to the homepage of the Amazon Lightsail console. Click the &lt;strong&gt;Account&lt;/strong&gt; from the Account menu dropdown. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feekhwrx9jczoyb4o5bwy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feekhwrx9jczoyb4o5bwy.png" alt="CleanShot 2022-05-28 at 19.10.54@2x.png" width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the &lt;strong&gt;Advanced&lt;/strong&gt; tab from the Account settings page. You will see a list of your VPCs. Connect the one that resides in your Redis cluster. For me, it's *&lt;em&gt;Virginia (us-east-1)&lt;br&gt;
*&lt;/em&gt;. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo9o7ujh6ifrysht82xay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo9o7ujh6ifrysht82xay.png" alt="CleanShot 2022-05-28 at 19.12.35@2x.png" width="800" height="666"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Connect to the Redis cluster from the Lightsail instance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using CLI
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Connect to the instance using SSH. &lt;a href="https://blog.harun.dev/deploy-wordpress-app-to-amazon-lightsail#heading-connect-to-the-instance-using-ssh" rel="noopener noreferrer"&gt;Follow this&lt;/a&gt; to connect to SSH.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Redis CLI &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A. On Amazon Linux 2
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nb"&gt;sudo &lt;/span&gt;amazon-linux-extras &lt;span class="nb"&gt;install &lt;/span&gt;epel &lt;span class="nt"&gt;-y&lt;/span&gt;
  &lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;gcc jemalloc-devel openssl-devel tcl tcl-devel &lt;span class="nt"&gt;-y&lt;/span&gt;
  &lt;span class="nb"&gt;sudo &lt;/span&gt;wget http://download.redis.io/redis-stable.tar.gz
  &lt;span class="nb"&gt;sudo tar &lt;/span&gt;xvzf redis-stable.tar.gz
  &lt;span class="nb"&gt;cd &lt;/span&gt;redis-stable
  &lt;span class="nb"&gt;sudo &lt;/span&gt;make &lt;span class="nv"&gt;BUILD_TLS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;B. on Ubuntu
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;redis-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Now connect to the Redis cluster. The moment of truth.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;redis-cli &lt;span class="nt"&gt;-h&lt;/span&gt; &amp;lt;redis_endpoint&amp;gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 6379
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;&amp;lt;redis_endpoint&amp;gt;&lt;/code&gt; with your Primary endpoint of Redis you copied in the clipboard. Remove port (":6379") from the endpoint URL. You will see this screen if successful 👇 &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxl5y0gjxpye7hpb67z54.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxl5y0gjxpye7hpb67z54.png" alt="CleanShot 2022-05-28 at 20.10.24@2x.png" width="800" height="219"&gt;&lt;/a&gt;&lt;br&gt;
You can use the &lt;code&gt;ping&lt;/code&gt;, &lt;code&gt;set&lt;/code&gt;, and &lt;code&gt;get&lt;/code&gt; commands to test it out. &lt;/p&gt;

&lt;h3&gt;
  
  
  Connect to Application
&lt;/h3&gt;

&lt;p&gt;You will get packages for most of the Applications, languages, and Frameworks. Check docs. For Laravel, you just have to configure it in the &lt;code&gt;.env&lt;/code&gt; file.  &lt;/p&gt;

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

&lt;p&gt;Hope you enjoyed this short article. You can subscribe to my newsletter, and follow me on &lt;a href="https://twitter.com/HarunRRayhan" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://dev.to/harunrrayhan"&gt;Dev.to&lt;/a&gt;, and &lt;a href="https://hashnode.com/@HarunRRayhan" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>redis</category>
      <category>vpc</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Add Lightsail Load Balancer to Application hosted in Amazon Lightsail instance(s)</title>
      <dc:creator>Harun R. Rayhan</dc:creator>
      <pubDate>Sat, 28 May 2022 11:12:16 +0000</pubDate>
      <link>https://dev.to/aws-builders/add-lightsail-load-balancer-to-application-hosted-in-amazon-lightsail-instances-3eh</link>
      <guid>https://dev.to/aws-builders/add-lightsail-load-balancer-to-application-hosted-in-amazon-lightsail-instances-3eh</guid>
      <description>&lt;p&gt;Lightsail offers Load Balancer. It's very easy to provision one and attach one or multiple instances to it. Let's create one and attach instances to it. &lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need one or more Amazon Lightsail instances to attach to the load balancer in the same region. You can create it by following &lt;a href="https://blog.harun.dev/deploy-laravel-application-to-amazon-lightsail" rel="noopener noreferrer"&gt;&lt;strong&gt;Deploy Laravel Application to Amazon Lightsail&lt;/strong&gt;&lt;/a&gt; or  &lt;a href="https://blog.harun.dev/deploy-wordpress-app-to-amazon-lightsail" rel="noopener noreferrer"&gt;&lt;strong&gt;Deploy WordPress App to Amazon Lightsail&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Warning: Amazon Light Load Balancer going to cost you &lt;strong&gt;USD$18/month&lt;/strong&gt;. It has no pre-tier available. Be sure before going to provision one. You can check the whole article before creating.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Provision a Load Balanced
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to the homepage of the Amazon Lightsail console. Click on the &lt;strong&gt;Networking&lt;/strong&gt; tab and click on the &lt;strong&gt;Create load balancer&lt;/strong&gt; button.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpwkehaaq1mn9bcd6wwo9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpwkehaaq1mn9bcd6wwo9.png" alt="CleanShot 2022-05-28 at 16.02.40@2x.png" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You are in the Create a Load balancer screen like this 👇&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh95y3jpxn6qj0sie3jo3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh95y3jpxn6qj0sie3jo3.png" alt="CleanShot 2022-05-28 at 16.04.46@2x.png" width="800" height="1068"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A. Change your region to where your instances are. My instances are in Virginia.&lt;/li&gt;
&lt;li&gt;B. Give it a name, and&lt;/li&gt;
&lt;li&gt;C. Hit the &lt;strong&gt;Create load balancer&lt;/strong&gt; button from the bottom.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Load balancer will be created in a moment and you should land on the Load balancer page.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Attach instances to the Load balance
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to the homepage again and then click the &lt;strong&gt;Networking&lt;/strong&gt; tab. You should see Loadbalancer(s) including the one just created. Click on the name of the Load balancer.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh3eceww5n0fz7tarpaj5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh3eceww5n0fz7tarpaj5.png" alt="CleanShot 2022-05-28 at 16.13.01@2x.png" width="799" height="573"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Our load balancer doesn't have any attached instance(s) yet, we are going to attach one or more. Select your instance in the Target instances section. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F08w9aw1ptyg54ai4jniy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F08w9aw1ptyg54ai4jniy.png" alt="CleanShot 2022-05-28 at 16.15.02@2x.png" width="800" height="675"&gt;&lt;/a&gt;&lt;br&gt;
I just have one in this region. You should see all of the instances you have in the region where the load balancer is provisioned. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Confirm the attachment of your instance&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8gc4vl1dl4lymtt78ntu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8gc4vl1dl4lymtt78ntu.png" alt="CleanShot 2022-05-28 at 16.19.15@2x.png" width="800" height="639"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do the same process until you attached all of your target instances. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to Load balancer URL (&lt;strong&gt;DNS name&lt;/strong&gt;) and you should see your application. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv5wvte2ypkbuvawifnx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv5wvte2ypkbuvawifnx1.png" alt="CleanShot 2022-05-28 at 16.26.00@2x.png" width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ If you don't see the desired page, check your application or other tools (Apache, Nginx, etc.) setting. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Add Domain or Sub-Domain
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You can create DNS Zone in Lightsail for your domain and configure it. &lt;a href="https://lightsail.aws.amazon.com/ls/docs/en_us/articles/add-alias-record-for-lightsail-load-balancer" rel="noopener noreferrer"&gt;Follow this to add your domain/sub-domain&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can add the Load balancer URL (&lt;strong&gt;DNS name&lt;/strong&gt;) as A record in your domain's DNS control panel.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Enable HTTPS
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;strong&gt;Inbound traffic&lt;/strong&gt; tab of your Load balancer setting. 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj5jm4pch3b2exa6wls8z.png" alt="CleanShot 2022-05-28 at 16.35.34@2x.png" width="800" height="640"&gt;

&lt;ul&gt;
&lt;li&gt;A. Create an SSL certificate for your domain by clicking the &lt;strong&gt;Create certificate +&lt;/strong&gt; button.&lt;/li&gt;
&lt;li&gt;B. You should see all of the created certificates in the &lt;strong&gt;SSL/TLS certificate&lt;/strong&gt; list and select the one you just created. &lt;/li&gt;
&lt;li&gt;C. Optionally, you can Redirect from HTTP to HTTPS. (After enabling the HTTPS).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Hope you enjoyed this short article. You can subscribe to my newsletter, and follow me on &lt;a href="https://twitter.com/HarunRRayhan" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://dev.to/harunrrayhan"&gt;Dev.to&lt;/a&gt;, and &lt;a href="https://hashnode.com/@HarunRRayhan" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>lightsail</category>
      <category>ec2</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Add Database to Application hosted in Amazon Lightsail</title>
      <dc:creator>Harun R. Rayhan</dc:creator>
      <pubDate>Mon, 23 May 2022 14:04:33 +0000</pubDate>
      <link>https://dev.to/aws-builders/add-database-to-application-hosted-in-amazon-lightsail-1lgk</link>
      <guid>https://dev.to/aws-builders/add-database-to-application-hosted-in-amazon-lightsail-1lgk</guid>
      <description>&lt;p&gt;Amazon Lightsail offers dedicated Databases. You can make use of it instead of an on-instance database. It's easy, managed, secure, and most importantly it's not tied to the instance. So, your database will be safe even if instance crashes.&lt;/p&gt;

&lt;p&gt;In "&lt;strong&gt;&lt;a href="https://blog.harun.dev/deploy-wordpress-app-to-amazon-lightsail" rel="noopener noreferrer"&gt;Deploy WordPress App to Amazon Lightsail&lt;/a&gt;&lt;/strong&gt;, the database is installed in the instance. In this article, we are to cover the dedicated databases.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Warning: Dedicated Lightsail Databases are going to cost $$$. It has a 3-month free tier at this time of writing. Check your plan before creating a database.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, what are you waiting for? Let's dive right in 🏃‍♂️&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Database Instance
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to your Amazon Lightsail home page. And then click on the &lt;strong&gt;Databases&lt;/strong&gt; tab.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now click on the &lt;strong&gt;Create database&lt;/strong&gt; button&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7qpinzj8c1skym8eur1t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7qpinzj8c1skym8eur1t.png" alt="CleanShot 2022-05-21 at 17.17.16@2x.png" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now you are on the database creation page: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzg5zqdq39ha30apdghnh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzg5zqdq39ha30apdghnh.png" alt="CleanShot 2022-05-21 at 17.23.57@2x.png" width="800" height="675"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(A) Select AWS Region and Availablity Zone (AZ). for me Virginia and Zone A are perfect.&lt;/li&gt;
&lt;li&gt;(B) Choose the Database engine and version. I selected MySQL 8.&lt;/li&gt;
&lt;li&gt;(C) Click on &lt;strong&gt;Specify login credentials&lt;/strong&gt;, and&lt;/li&gt;
&lt;li&gt;(D) Click on &lt;strong&gt;Specify the master database name&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Scroll down a little and configure this: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2dhtuc75g4zb0qkv2irv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2dhtuc75g4zb0qkv2irv.png" alt="CleanShot 2022-05-21 at 18.03.04@2x.png" width="800" height="1370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(A) Choose Database username&lt;/li&gt;
&lt;li&gt;(B) Password. I'm okay with the generated password, but you can choose yours.&lt;/li&gt;
&lt;li&gt;(C) The first/master database. You can create more later.&lt;/li&gt;
&lt;li&gt;(D &amp;amp; E) Choose your plan type and the actual plan. You can choose the free-tier option like me.&lt;/li&gt;
&lt;li&gt;(F) Give it a name, and&lt;/li&gt;
&lt;li&gt;(G) Hit the &lt;strong&gt;Create database&lt;/strong&gt; button
Your database should be created within a few minutes. Please wait for it until it's &lt;strong&gt;Running&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Database Credentials
&lt;/h2&gt;

&lt;p&gt;You already know your database username and master database. For everything else, go to your database and click on the &lt;strong&gt;Connect&lt;/strong&gt; tab. You should see your credentials like this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft8q84ag0ublpic45dr72.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft8q84ag0ublpic45dr72.png" alt="CleanShot 2022-05-21 at 18.24.30@2x.png" width="800" height="639"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(A) DB Username&lt;/li&gt;
&lt;li&gt;(B) DB Password&lt;/li&gt;
&lt;li&gt;(C) DB Endpoint&lt;/li&gt;
&lt;li&gt;(D) DB Port&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Connect to the database
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Instance CLI
&lt;/h3&gt;

&lt;p&gt;You can &lt;a href="https://blog.harun.dev/deploy-laravel-application-to-amazon-lightsail#heading-connect-to-the-instance-using-ssh" rel="noopener noreferrer"&gt;SSH into your instance&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Install MySQL using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;mysql-server &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start mysql.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then connect to MySQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysql &lt;span class="nt"&gt;--host&lt;/span&gt; &amp;lt;db_endpoint&amp;gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &amp;lt;db_username&amp;gt; &lt;span class="nt"&gt;--password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be asked for your DB password. If successful, you will see this screen:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe6260u3p6e8quqnqkrel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe6260u3p6e8quqnqkrel.png" alt="CleanShot 2022-05-21 at 18.35.21@2x.png" width="799" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  SQL Client App over SSH (SSH Tunneling)
&lt;/h3&gt;

&lt;p&gt;Most modern SQL Client applications (ex &lt;a href="https://docs.tableplus.com/gui-tools/manage-connections#ssh-tunneling" rel="noopener noreferrer"&gt;TablePlus&lt;/a&gt;, MySQL Workbench) have the option to connect Database via SSH. You can connect using that option. &lt;/p&gt;

&lt;h2&gt;
  
  
  Add Database to your application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Laravel and NodeJS
&lt;/h3&gt;

&lt;p&gt;You can update your application's &lt;code&gt;.env&lt;/code&gt; file with Database credentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  WordPress
&lt;/h3&gt;

&lt;p&gt;Update your &lt;code&gt;wp-config.php&lt;/code&gt; file with the database credentials.&lt;/p&gt;

&lt;h3&gt;
  
  
  Any other app
&lt;/h3&gt;

&lt;p&gt;Update your database credential in your application's configuration and/or environment file. Check your application's documentation for that. Or it's custom, you know where to put it.&lt;/p&gt;

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

&lt;p&gt;Thanks for reading the article. Adding dedicated databases to Amazon Lightsail instance is not that hard. You can follow me to get instant updates on my posts. You can also subscribe to &lt;a href="https://blog.harun.dev" rel="noopener noreferrer"&gt;my newsletter&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>lightsail</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Deploy Laravel Application to Amazon Lightsail</title>
      <dc:creator>Harun R. Rayhan</dc:creator>
      <pubDate>Thu, 19 May 2022 15:06:43 +0000</pubDate>
      <link>https://dev.to/aws-builders/deploy-laravel-application-to-amazon-lightsail-5e8g</link>
      <guid>https://dev.to/aws-builders/deploy-laravel-application-to-amazon-lightsail-5e8g</guid>
      <description>&lt;p&gt;Beginners friendly step-by-step guide to deploy any application to Amazon Lightsail. This article demos a Laravel app but the idea is the same for all kind of applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Amazon Lightsail is a fixed monthly priced VPS service offered by AWS. Once you install the OS of your choice, you can run almost any application. In this application, I'm showing how to deploy one of my favorite backend frameworks called Laravel. Which most of you might hear of. Let's deploy it.&lt;/p&gt;

&lt;h2&gt;
  
  
  I don't like/know Laravel
&lt;/h2&gt;

&lt;p&gt;It's totally fine if you don't like or know Laravel. Laravel is just the framework I choose to demo but it can be any app. It can be Node.js, Python, Django, Fast API, Ruby on Rails, or any other app. The idea is the same, but some of the steps in deployment will be different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need a Laravel app to deploy. Don't worry if you don't have one, I have to best &lt;a href="https://github.com/HarunRay/laravel-to-lightsail" rel="noopener noreferrer"&gt;Laravel (v9) app&lt;/a&gt; you can find in the internet (pun intended).&lt;/p&gt;

&lt;p&gt;Let's deploy our Laravel app. 📦👉☁️&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Amazon Lightsail Instance
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to Lightsail homepage. Click on the &lt;strong&gt;Create instance&lt;/strong&gt; button under &lt;strong&gt;Instances&lt;/strong&gt; tab.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4g2e9xbume0cgzcmo7dz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4g2e9xbume0cgzcmo7dz.png" alt="CleanShot 2022-05-18 at 19.44.36@2x.png" width="799" height="385"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will land on the Create instance page. We will use Ubuntu for this demo but you are open to choosing the OS of your choice. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1652882301357%2FkHB-fJG9L.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1652882301357%2FkHB-fJG9L.png" alt="CleanShot 2022-05-18 at 19.51.37@2x.png" width="800" height="2293"&gt;&lt;/a&gt;&lt;br&gt;
(A) Change the AWS Region and Availablity Zone (AZ) of your choice if not the correct one selected. (B) Select &lt;strong&gt;Linux/Unix&lt;/strong&gt; platform. (C) Select &lt;strong&gt;Ubuntu (20.04 LTS)&lt;/strong&gt; as OS. The blueprint should be &lt;strong&gt;OS Only&lt;/strong&gt;. (D) Choose a plan. (E) Give it a name, and (F) Hit the &lt;strong&gt;Create instance&lt;/strong&gt; button from the bottom of the page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Your instance is in the &lt;strong&gt;Pending&lt;/strong&gt; state. Will be &lt;strong&gt;Running&lt;/strong&gt; in a few minutes. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Assign a static IP
&lt;/h2&gt;

&lt;p&gt;Read &lt;a href="https://blog.harun.dev/deploy-wordpress-app-to-amazon-lightsail#heading-assign-a-static-ip-optional" rel="noopener noreferrer"&gt;&lt;strong&gt;how to assign a Static IP to an Amazon Lightsail Instance&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect to the instance using SSH
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ I described in detail &lt;a href="https://blog.harun.dev/deploy-wordpress-app-to-amazon-lightsail#heading-connect-to-the-instance-using-ssh" rel="noopener noreferrer"&gt;How to connect to an Amazon Lightsail instance using SSH&lt;/a&gt;. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to the instance page by clicking on the name. And go to the &lt;strong&gt;Connect&lt;/strong&gt; tab. We need these 3 things: (A) Public IP, (B) SSH Username, and (C) Download the SSH Key. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu9fa0oqfweat7jv68jt4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu9fa0oqfweat7jv68jt4.png" alt="CleanShot 2022-05-18 at 20.21.12@2x.png" width="800" height="725"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Change permission of Key file:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Change "path/to/keyfile.pem" with your key file name&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;400 path/to/keyfile.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Connect to the SSH
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#format &lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"path/to/keyfile.pem"&lt;/span&gt; &amp;lt;username&amp;gt;@&amp;lt;ip_address&amp;gt;
&lt;span class="c"&gt;# Should look like this with your own value&lt;/span&gt;
&lt;span class="c"&gt;# ssh -i "LightsailDefaultKey-us-east-1.pem" ubuntu@52.201.59.133&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;If you are asked to add IP to known hosts, type &lt;code&gt;yes&lt;/code&gt; and you should be logged in. &lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Installing Necessary Scripts
&lt;/h2&gt;

&lt;p&gt;Our server is created and it's empty. You can install any script you want, depending on what you need. In this demo, we are going to use Laravel. You can follow along even if you have a different app/stack.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Update package information
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Some packages
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;curl git unzip &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Install PHP 
I love the latest PHP 8.1. It's not officially available in Ubuntu 20.04 yet. We will enable the PHP Repository to install PHP 8.1.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;software-properties-common &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;add-apt-repository ppa:ondrej/php &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;php8.1-fpm php8.1-cli php8.1-mysql php8.1-curl php-xml php-mbstring &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check php version by using &lt;code&gt;php --version&lt;/code&gt; command. You should see something like this: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjj2qitadw2r6jplwt5nq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjj2qitadw2r6jplwt5nq.png" alt="CleanShot 2022-05-18 at 21.02.08@2x.png" width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Composer
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Download the bin file&lt;/span&gt;
curl &lt;span class="nt"&gt;-sS&lt;/span&gt; https://getcomposer.org/installer | php
&lt;span class="c"&gt;# Move it to correct path&lt;/span&gt;
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;composer.phar /usr/local/bin/composer
&lt;span class="c"&gt;# Make it executeable&lt;/span&gt;
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; +x /usr/local/bin/composer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Install Nginx &amp;amp; configure nginx
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nginx &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Clone your application repo
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# go to www directory&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /var/www/html
&lt;span class="c"&gt;# clone the git repo&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;git clone https://github.com/HarunRay/laravel-to-lightsail.git
&lt;span class="c"&gt;# go to application directory&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;laravel-to-lightsail
&lt;span class="c"&gt;# copy .env.example file&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;composer &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Create a nginx config file
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim /etc/nginx/sites-available/laravel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Paste this code. Change &lt;code&gt;52.201.59.133&lt;/code&gt; to your IP address and save.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="s"&gt;[::]:80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="mf"&gt;52.201&lt;/span&gt;&lt;span class="s"&gt;.59.133&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/var/www/html/laravel-to-lightsail/public&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-Frame-Options&lt;/span&gt; &lt;span class="s"&gt;"SAMEORIGIN"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-Content-Type-Options&lt;/span&gt; &lt;span class="s"&gt;"nosniff"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;index&lt;/span&gt; &lt;span class="s"&gt;index.php&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;charset&lt;/span&gt; &lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="n"&gt;/index.php?&lt;/span&gt;&lt;span class="nv"&gt;$query_string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;/favicon.ico&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kn"&gt;log_not_found&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;/robots.txt&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kn"&gt;access_log&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="kn"&gt;log_not_found&lt;/span&gt; &lt;span class="no"&gt;off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;error_page&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="n"&gt;/index.php&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;\.php$&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;fastcgi_pass&lt;/span&gt; &lt;span class="s"&gt;unix:/var/run/php/php8.1-fpm.sock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;fastcgi_param&lt;/span&gt; &lt;span class="s"&gt;SCRIPT_FILENAME&lt;/span&gt; &lt;span class="nv"&gt;$realpath_root$fastcgi_script_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;include&lt;/span&gt; &lt;span class="s"&gt;fastcgi_params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt; &lt;span class="sr"&gt;/\.(?!well-known).*&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;deny&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, run this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# link nginx config &lt;/span&gt;
&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /etc/nginx/sites-available/laravel /etc/nginx/sites-enabled/
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start nginx
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Set Laravel App Permission
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Go to your laravel app directory&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /var/www/html/laravel-to-lightsail/
&lt;span class="c"&gt;# Add files &amp;amp; folder www-data user &amp;amp; group&lt;/span&gt;
&lt;span class="nb"&gt;sudo chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; www-data:www-data &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="c"&gt;# Add your ubuntu to group&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; www-data ubuntu
&lt;span class="c"&gt;# Set file(s) permission&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;644 &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;span class="c"&gt;# Set folder(s) permission&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; d &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;chmod &lt;/span&gt;755 &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;span class="c"&gt;# Set cache directory permission&lt;/span&gt;
&lt;span class="nb"&gt;sudo chgrp&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; www-data storage bootstrap/cache
&lt;span class="nb"&gt;sudo chmod&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; ug+rwx storage bootstrap/cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Generate app key
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;php artisan key:generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Update the &lt;code&gt;.env&lt;/code&gt; file
Open file
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim /var/www/html/laravel-to-lightsail/.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, change these values;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;APP_ENV=production
# Change IP to yours
APP_URL=http://52.201.59.133
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now go to your public IP address. Mine is &lt;code&gt;http://52.201.59.133&lt;/code&gt;. You should see this welcome page. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcnhmlg0rglkkvssgofej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcnhmlg0rglkkvssgofej.png" alt="CleanShot 2022-05-19 at 20.50.16@2x.png" width="799" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations, your Laravel app launched on Amazon Lightsail instance. 🚀&lt;/p&gt;

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

&lt;p&gt;In the next few articles, we are going to configure the database, load balancer &amp;amp; Redis (Elasticache). Follow me and subscribe to &lt;a href="https://blog.harun.dev" rel="noopener noreferrer"&gt;my newsletter&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>lightsail</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Snapshot and Restore of Lightsail Instance to Lightsail/EC2</title>
      <dc:creator>Harun R. Rayhan</dc:creator>
      <pubDate>Thu, 12 May 2022 14:31:04 +0000</pubDate>
      <link>https://dev.to/aws-builders/snapshot-and-restore-of-lightsail-instance-to-lightsailec2-l0j</link>
      <guid>https://dev.to/aws-builders/snapshot-and-restore-of-lightsail-instance-to-lightsailec2-l0j</guid>
      <description>&lt;p&gt;Taking regular backup/snapshots of a production application is important. It is one of the main requirements of a disaster recovery(DR) plan. But snapshots are not only for DR, you can use them to move/clone instances to different regions or availability zone(AZ). Maybe move to a higher spec instance. Or maybe you want to move to Amazon EC2 altogether. &lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You just need an existing Amazon Lightsail instance. Don't worry if you don't have one, follow how to &lt;a href="https://blog.harun.dev/deploy-wordpress-app-to-amazon-lightsail" rel="noopener noreferrer"&gt;&lt;strong&gt;Deploy WordPress App to Amazon Lightsail&lt;/strong&gt;&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Take Snapshot (Backup)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Click on the instance name you want to take a snapshot. All of the instances are listed under the &lt;strong&gt;Instances&lt;/strong&gt; tab of the home page. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzl17nikbzgp4r55fck7f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzl17nikbzgp4r55fck7f.png" alt="Amazon Lightsail Instances" width="799" height="341"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to the &lt;strong&gt;Snapshots&lt;/strong&gt; tab of your instance page. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftnui824ad5l8nds45b1c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftnui824ad5l8nds45b1c.png" alt="Amazon Lightsail Instance Snapshot" width="800" height="498"&gt;&lt;/a&gt;&lt;br&gt;
(B) We are going to create a manual snapshot. This one will take a snapshot instantly.&lt;br&gt;
(C) Optionally, you can set up an automatic snapshot too. It will take daily snapshots. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;em&gt;**For production instance, always set up an automatic snapshot. *&lt;/em&gt;*&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Give it a name and click &lt;strong&gt;Create&lt;/strong&gt;. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvwx7e8ft7lwlc9tv5fx8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvwx7e8ft7lwlc9tv5fx8.png" alt="Amazon Lightsail Instance Snapshot" width="800" height="351"&gt;&lt;/a&gt;&lt;br&gt;
It will take some time if this is the first snapshot of the instance, size is another factor. Subsequent snapshots are faster, as it takes incremental backups, only changes from the last snapshot. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upon completion, it will look like this: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvrsrr31l14eu1sqmcsul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvrsrr31l14eu1sqmcsul.png" alt="Amazon Lightsail Instance Snapshot" width="800" height="341"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will find all of the snapshots in the &lt;strong&gt;Snapshots&lt;/strong&gt; tab of the homepage. I have just one: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiua55kxtv1yvsyf4z173.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiua55kxtv1yvsyf4z173.png" alt="Amazon Lightsail all snapshots" width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Restore to new Amazon Lightsail instance
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Click on these 👇 three dots: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frbe6wwjbdeakupxiylh1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frbe6wwjbdeakupxiylh1.png" alt="Lightsail Snapshot restore menu" width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on &lt;strong&gt;Create new instance&lt;/strong&gt; from this menu: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm9q449wi6rnnwsjo1oez.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm9q449wi6rnnwsjo1oez.png" alt="Lightsail Snapshot restore menu" width="799" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ You can click on the second item, &lt;strong&gt;Copy to another Region&lt;/strong&gt; to literally copy to another region and restore to a new instance there. Snapshot stored in an S3 bucket in the same region where the instance launched. You can't access the S3 bucket as it's managed by AWS. To restore a snapshot to a new instance in another region, you have to copy it first.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The page looks familiar, right? It's the same create instance page.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo433z2g2c6m75afpqqnb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo433z2g2c6m75afpqqnb.png" alt="Create an instance from a snapshot in Amazon Lightsail" width="800" height="1511"&gt;&lt;/a&gt;&lt;br&gt;
(A) You can change the availability zone(AZ) if you want. It's great if you are using multiple instances for horizontal scaling, gives high availability(HA). &lt;br&gt;
(B) Choose an instance the same size or larger than the source. Choose larger for vertical scaling. If you need a larger instance than you originally have, this is the way.&lt;br&gt;
(C) Give it a name, and &lt;br&gt;
(D) Click the &lt;strong&gt;Create instance&lt;/strong&gt; button from the bottom of the page.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wait a moment, maybe a few minutes. Congratulations 🎉 you just restored your instance. &lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;h2&gt;
  
  
  Copy to another Region
&lt;/h2&gt;

&lt;p&gt;Snapshot stored in an S3 bucket in the same region where the instance launched. You can't access the S3 bucket as it's managed by AWS. To restore a snapshot to a new instance in another region, you have to copy it first. Let's copy it to another region.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to the &lt;strong&gt;Snapshots&lt;/strong&gt; tab of the homepage. Find your instance and snapshot. Click the three dots. And then click &lt;strong&gt;Copy to another Region&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb3osw9rmvvo28wabqmup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb3osw9rmvvo28wabqmup.png" alt="Copy snapshot to another Region in Amazon Lightsail" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(A) Choose the region you want to copy the snapshot, (B) give it a name, and (C) click the &lt;strong&gt;Copy snapshot&lt;/strong&gt; button from the bottom of the page. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F80z5sqsji77q90h2fada.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F80z5sqsji77q90h2fada.png" alt="Copy snapshot to another Region in Amazon Lightsail" width="800" height="949"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It will take a few minutes to copy the snapshot.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;h2&gt;
  
  
  Restore/Export to Amazon EC2 instance
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Click three dots from the snapshot menu you want to restore to EC2 and click the &lt;strong&gt;Export to Amazon EC2&lt;/strong&gt; menu item &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F57nqonmp2uki78l7wvz6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F57nqonmp2uki78l7wvz6.png" alt="Export to Amazon EC2" width="800" height="634"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click the &lt;strong&gt;Yes, continue&lt;/strong&gt; button in the popup. Click &lt;strong&gt;Acknowledge&lt;/strong&gt; in the next popup.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1652281327886%2FUOQteEtmX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1652281327886%2FUOQteEtmX.png" alt="Export to Amazon EC2" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ If you are not in the free-tier or cross the free-tier limit, you may get charged for this. If you are not sure, don't do it. Just follow my demo.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will see a task running like this ↓. Wait for a while to finish. A small snapshot wouldn't take much time. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd48inutgv8revwijywps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd48inutgv8revwijywps.png" alt="Export to Amazon EC2" width="799" height="305"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After the copy task is completed, go to &lt;a href="https://us-east-1.console.aws.amazon.com/ec2/v2/home?region=us-east-1#Snapshots:" rel="noopener noreferrer"&gt;Snapshots of EC2 Management Console&lt;/a&gt;. Choose the correct region. You should see your snapshot there.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the snapshot and click the &lt;strong&gt;Create image from snapshot&lt;/strong&gt; from the &lt;strong&gt;Actions&lt;/strong&gt; menu &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2sossqqhp2mbxju49ld2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2sossqqhp2mbxju49ld2.png" alt="Snapshots on EC2 Management Console" width="799" height="189"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Give the name of the image and click the &lt;strong&gt;Create image&lt;/strong&gt; button from the bottom of the page&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuqkly1qo1kr9etkxq098.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuqkly1qo1kr9etkxq098.png" alt="Snapshots on EC2 Management Console" width="800" height="714"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now go to &lt;a href="https://us-east-1.console.aws.amazon.com/ec2/v2/home?region=us-east-1#Images:visibility=owned-by-me" rel="noopener noreferrer"&gt;AMIs page of EC2 Management Console&lt;/a&gt;. Choose the correct region, N. Virginia (us-east-1) in my case. You should see all of your AMIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the AMIs you just created and click the &lt;strong&gt;Lunch Instance from AMI&lt;/strong&gt; button in the top right corner. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fboqlk0gig286la807ngs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fboqlk0gig286la807ngs.png" alt="CleanShot 2022-05-12 at 19.34.19@2x.png" width="799" height="202"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will see a traditional EC2 Lunch page: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1652363162708%2FwxsZxlGt6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1652363162708%2FwxsZxlGt6.png" alt="CleanShot 2022-05-12 at 19.44.03@2x.png" width="800" height="2367"&gt;&lt;/a&gt;&lt;br&gt;
(A) Give your instance a name. (B) Choose an instance type, for my case, it's free tier eligible t2.micro. (C) Choose existing key pair or create a new one. You need it to log in via SSH. (D) Check the ports you want to open, in my case I selected all of them. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, click the &lt;strong&gt;Lunch instance&lt;/strong&gt; button from the right sidebar. Wait for a while to instance state to become &lt;strong&gt;&lt;em&gt;Running&lt;/em&gt;&lt;/strong&gt; and Status check &lt;strong&gt;&lt;em&gt;2/2 checks passed&lt;/em&gt;&lt;/strong&gt;. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You will see the EC2 Instances page like this:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5wyfn2y7z7wfgmffki4h.png" alt="CleanShot 2022-05-12 at 19.56.19@2x.png" width="800" height="430"&gt;
Select the instance. Copy the public IP address and try it in the browser. You should see the same page as Amazon Lightsail instance. &lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ Manual snapshots will not be deleted even if you delete the instance. So, don't forget to delete unnecessary snapshots to save bills.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;...&lt;/p&gt;

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

&lt;p&gt;Hope you enjoyed this article. Ask your questions in the comment. Subscribe to my newsletter to get weekly updates. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>ec2</category>
      <category>lightsail</category>
    </item>
    <item>
      <title>Add (Cloudfront) CDN Distribution to WordPress Application hosted on Amazon Lightsail</title>
      <dc:creator>Harun R. Rayhan</dc:creator>
      <pubDate>Tue, 10 May 2022 12:15:16 +0000</pubDate>
      <link>https://dev.to/aws-builders/add-cloudfront-cdn-distribution-to-wordpress-application-hosted-on-amazon-lightsail-4hlh</link>
      <guid>https://dev.to/aws-builders/add-cloudfront-cdn-distribution-to-wordpress-application-hosted-on-amazon-lightsail-4hlh</guid>
      <description>&lt;p&gt;It's not very hard to add CDN Distribution to a WordPress app hosted on Amazon Lightsail. But if you are new, it can be cumbersome. This article is to demonstrate how to do it easily. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why Should I use CDN distribution?
&lt;/h2&gt;

&lt;p&gt;You might hear about Content Delivery Network (CDN). If not, here is an article explaining "&lt;a href="https://www.cloudflare.com/en-gb/learning/cdn/what-is-a-cdn/" rel="noopener noreferrer"&gt;what is CDN?&lt;/a&gt;". &lt;/p&gt;

&lt;p&gt;If you are not interested to read a long article, here is TL;DR: &lt;br&gt;
In CDN, Lots of small servers run closer to your customer. Content gets cached there and served from the closer server(s) of your customer. In that way, ease the pressure from the main servers. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is CDN Distribution?
&lt;/h2&gt;

&lt;p&gt;It is the Amazon Lightsail CDN service. Under the hood, it uses Amazon Cloudfront. It has 300+ points of presence to deliver content closer to your customers. On top of that, it comes with AWS Shield, which protects infrastructure from DDoS attacks. &lt;/p&gt;

&lt;p&gt;Are you excited to create your first CDN Distribution? Let's go 🏃‍♂️&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;h3&gt;
  
  
  WordPress Application
&lt;/h3&gt;

&lt;p&gt;You need to have an existing WordPress application for this tutorial. Don't worry if you don't have one. I covered How to &lt;strong&gt;&lt;a href="https://blog.harun.dev/deploy-wordpress-app-to-amazon-lightsail" rel="noopener noreferrer"&gt;Deploy WordPress Application to Amazon Lightsail&lt;/a&gt;&lt;/strong&gt; in the first part of this series. &lt;/p&gt;

&lt;h3&gt;
  
  
  Attach a Static IP to WordPress Instance
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to the &lt;strong&gt;Networking&lt;/strong&gt; tab  and then click on &lt;strong&gt;Create static IP&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1dzyhwk4jeg0w5f3v5wj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1dzyhwk4jeg0w5f3v5wj.png" alt="Create static IP on Amazon Lightsail" width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(A) Select the WP App instance. (B) Give it a meaningful name and (C) Hit &lt;strong&gt;Create&lt;/strong&gt;.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Favy66pod9jplgn1uiqza.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Favy66pod9jplgn1uiqza.png" alt="Create static IP on Amazon Lightsail 2" width="800" height="710"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now we are ready to create our first distributions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create CDN Distribution
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;a href="https://lightsail.aws.amazon.com/ls/webapp/home/networking" rel="noopener noreferrer"&gt;home page&lt;/a&gt; of Lightsail. Go to &lt;strong&gt;Network&lt;/strong&gt; tab again. This time hit the &lt;strong&gt;Create distribution&lt;/strong&gt; button. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fms71mefca6htjtionunc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fms71mefca6htjtionunc.png" alt="Create CDN Distribution on Amazon Lightsail" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the WordPress Instance as Origin &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbl2ftp76sm0hc1ky8o5y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbl2ftp76sm0hc1ky8o5y.png" alt="Amazon Lightsail CDN Distribution Origin" width="800" height="651"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hit &lt;strong&gt;Yes, apply&lt;/strong&gt; to apply the predefined WordPress settings&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv884ak4l58xj6ltmlazn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv884ak4l58xj6ltmlazn.png" alt="Amazon Lightsail CDN Distribution WordPress" width="800" height="729"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now, (A) choose a plan. (B) Give it a name and (C) Hit &lt;strong&gt;Create Distribution&lt;/strong&gt; button&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo98odc87vfdrdfxxcxra.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo98odc87vfdrdfxxcxra.png" alt="CleanShot 2022-05-09 at 20.37.34@2x.png" width="800" height="769"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Woohoo 🎉 Your first distribution is created. Wait for a while to be available. After getting done, you will see this 👇 &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flb3kojymfhj209t976gt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flb3kojymfhj209t976gt.png" alt="Amazon Lightsail CDN Cloudfront Distribution " width="800" height="588"&gt;&lt;/a&gt;&lt;br&gt;
(A) Your domain for the distribution. You can access the site using this URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Domain
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Update the Domain in DB
&lt;/h3&gt;

&lt;p&gt;Login to your WordPress application's database, you may follow &lt;a href="https://blog.harun.dev/deploy-wordpress-app-to-amazon-lightsail#heading-import-database" rel="noopener noreferrer"&gt;this&lt;/a&gt;. Update the &lt;code&gt;siteurl&lt;/code&gt; and &lt;code&gt;home&lt;/code&gt; to your &lt;code&gt;wp_options&lt;/code&gt; table to your distribution domain. &lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You can use a custom domain instead of CloudFront's sub-domain. You may follow &lt;a href="https://lightsail.aws.amazon.com/ls/docs/en_us/articles/amazon-lightsail-enabling-distribution-custom-domains" rel="noopener noreferrer"&gt;this tutorial&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can add Object Storage to your WordPress app with a CDN distribution by following &lt;a href="https://aws.amazon.com/getting-started/hands-on/object-storage-cdn/" rel="noopener noreferrer"&gt;this tutorial&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Hope you enjoyed this simple article and created your first CDN distribution. Ask your questions in the comment. Subscribe to get the next blog post. Thanks 🙏 &lt;/p&gt;

</description>
      <category>aws</category>
      <category>wordpress</category>
      <category>cloud</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Deploy WordPress App to Amazon Lightsail</title>
      <dc:creator>Harun R. Rayhan</dc:creator>
      <pubDate>Sun, 08 May 2022 11:02:48 +0000</pubDate>
      <link>https://dev.to/aws-builders/deploy-wordpress-app-to-amazon-lightsail-48i</link>
      <guid>https://dev.to/aws-builders/deploy-wordpress-app-to-amazon-lightsail-48i</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;AWS Lightsail can be the easiest entry to the AWS Cloud. It has fixed monthly pricing unlike most other services, so you don't have to worry about a huge monthly bill. &lt;/p&gt;

&lt;p&gt;Lightsail is really great for small to medium apps. It has out-of-the-box support for &lt;strong&gt;WordPress&lt;/strong&gt;, Magento, Joomla, Prestashop, Drupal, Ghost, and Django apps. &lt;/p&gt;

&lt;p&gt;It provides pre-configured stacks like LAMP, NGINX, MEAN, and Node.js. You can also launch pre-configured Plesk, WHM &amp;amp; cPanel in just a few clicks. &lt;/p&gt;

&lt;p&gt;But that's not all. You can launch any Linux-based and Windows OS instance. My personal favorites are &lt;strong&gt;Ubuntu&lt;/strong&gt; and &lt;strong&gt;Amazon Linux 2&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Need a valid AWS account to deploy the WP app. If you don't have one, just create one from &lt;a href="https://portal.aws.amazon.com/billing/signup" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Before starting to deploy your app read about &lt;a href="https://aws.amazon.com/lightsail/pricing/" rel="noopener noreferrer"&gt;Amazon Lightsail Pricing and Free-Tier&lt;/a&gt;. Don't worry, I will try to keep everything within the free tier. At this time of writing, you can run a Lightsail instance for free for 3 months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install WordPress App
&lt;/h2&gt;

&lt;p&gt;Login to your AWS account if not already. Go to &lt;a href="https://lightsail.aws.amazon.com" rel="noopener noreferrer"&gt;Lightsail homepage&lt;/a&gt;. You should see a page like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr0gw33p1383a7fidlg0a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr0gw33p1383a7fidlg0a.png" alt="CleanShot 2022-04-27 at 17.59.20@2x.png" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Are you seeing this? Let's go 🏃‍♂️&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Instance
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Now click on the big &lt;strong&gt;Create Instance&lt;/strong&gt; button. If you don't see one, follow this: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkl7t9asc54ptvuqsv2si.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkl7t9asc54ptvuqsv2si.png" alt="CleanShot 2022-04-27 at 18.04.31@2x.png" width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Let's choose &lt;strong&gt;Instance location&lt;/strong&gt;, &lt;strong&gt;platform&lt;/strong&gt;, and &lt;strong&gt;blueprint&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyo6wc1baaxyodfy8egwa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyo6wc1baaxyodfy8egwa.png" alt="CleanShot 2022-04-27 at 18.20.07@2x.png" width="800" height="776"&gt;&lt;/a&gt;&lt;br&gt;
A. Click the link to change location and availability zone (AZ) if not what you want. I recommend selecting a location closer to your customers.&lt;br&gt;
B. Choose the &lt;strong&gt;Linux/Unix&lt;/strong&gt; platform.&lt;br&gt;
C. Select the &lt;strong&gt;Apps + OS&lt;/strong&gt; from the blueprint.&lt;br&gt;
D. Now choose &lt;strong&gt;WordPress&lt;/strong&gt; from the app list. &lt;em&gt;Not the multisite one&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now scroll down to &lt;strong&gt;Choose your instance plan&lt;/strong&gt; section. I chose a free-tier plan and only one instance. Here is my setup:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqks1t10sckkwy74i59l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpqks1t10sckkwy74i59l.png" alt="CleanShot 2022-04-27 at 19.04.37@2x.png" width="800" height="745"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally scroll to the bottom of the page and click the &lt;strong&gt;Create instance&lt;/strong&gt; button. Wait for the status to become &lt;strong&gt;&lt;em&gt;Running&lt;/em&gt;&lt;/strong&gt;. Congratulations, your instance has launched 🚀&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Assign a Static IP (optional)
&lt;/h3&gt;

&lt;p&gt;AWS Lightsail Instance comes with public (and private) IP. Which changes every time Stop and Start the instance.  My Public IP was this when I launched the instance: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsa3x3e4lpvjz3dkd7j5h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsa3x3e4lpvjz3dkd7j5h.png" alt="CleanShot 2022-04-27 at 19.24.43@2x.png" width="799" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After Stop and Start the instance, it got a new Public IP address: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8pf3hazuifhvnvtu92v2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8pf3hazuifhvnvtu92v2.png" alt="CleanShot 2022-04-27 at 19.31.30@2x.png" width="800" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have to fix that. So, let's create and assign a &lt;strong&gt;Static IP&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on your application instance name to see the details of your instance. &lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;+ Create static IP&lt;/strong&gt; from here:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhmww34ex5w9qvgli2fvt.png" alt="CleanShot 2022-04-27 at 19.35.25@2x.png" width="800" height="535"&gt;
&lt;/li&gt;
&lt;li&gt;Add a meaningful name and click Create:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb514enyad6011i5qsk7e.png" alt="CleanShot 2022-04-27 at 19.42.36@2x.png" width="800" height="768"&gt;
&amp;gt; ⚠️ Warning: You are allowed to assign 5 static IPs for free. You will be charged for static IP that is not attached to a running instance. &lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Connect to the instance using SSH
&lt;/h3&gt;

&lt;p&gt;You can connect to an instance in two ways: &lt;/p&gt;
&lt;h4&gt;
  
  
  Using browser
&lt;/h4&gt;

&lt;p&gt;That's the easiest. Go to the &lt;strong&gt;Connect&lt;/strong&gt; tab and click &lt;strong&gt;Connect using SSH&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc6ya2nyza5e5zs95p8v4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc6ya2nyza5e5zs95p8v4.png" alt="CleanShot 2022-04-27 at 20.07.41@2x.png" width="800" height="721"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Using SSH Client
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Download the SSH key from here: 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fek9dl5j0rp4xe4jbpk3u.png" alt="CleanShot 2022-04-27 at 20.10.12@2x.png" width="800" height="707"&gt;
&lt;/li&gt;
&lt;li&gt;If you are using PuTTY you can &lt;a href="https://lightsail.aws.amazon.com/ls/docs/en_us/articles/lightsail-how-to-set-up-putty-to-connect-using-ssh" rel="noopener noreferrer"&gt;follow this tutorial&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;If you are using a terminal in macOS or Linux, run this command on where you downloaded the SSH key.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run for the first time to fix the permission&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;400 &amp;lt;path/to/your-lightsail-ssh-key&amp;gt;.pem

&lt;span class="c"&gt;# Connect to your Lightsail instance&lt;/span&gt;
&lt;span class="c"&gt;# Username and Public IP can be found in "Connect" tab&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;path/to/your-lightsail-key&amp;gt;.pem"&lt;/span&gt; &amp;lt;username&amp;gt;@&amp;lt;public_ip&amp;gt;

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

&lt;/div&gt;

&lt;h3&gt;
  
  
  Get Admin Username and Password
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;You can get the username from the &lt;strong&gt;Connect&lt;/strong&gt; tab. The username is &lt;strong&gt;user&lt;/strong&gt; for this stack.&lt;/li&gt;
&lt;li&gt;To get the password, log in to SSH and run this command
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;bitnami_application_password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb2tji07qegx9ls7ijwv1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb2tji07qegx9ls7ijwv1.png" alt="CleanShot 2022-04-27 at 20.23.35@2x.png" width="800" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Now open a browser tab and go to &lt;code&gt;http://&amp;lt;public_ip&amp;gt;/wp-admin&lt;/code&gt;. Enter username and password above. You should see the WP Admin dashboard like me.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Add a Domain
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;strong&gt;Home&lt;/strong&gt; page. Click on the &lt;strong&gt;Networking&lt;/strong&gt; tab and then hit &lt;strong&gt;Create DNS zone&lt;/strong&gt; button
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgp52oiwdxqninus03sdu.png" alt="CleanShot 2022-04-28 at 04.57.42@2x.png" width="800" height="674"&gt;
&lt;/li&gt;
&lt;li&gt;Add your domain name. Scroll to the bottom and click the &lt;strong&gt;Create DNS zone&lt;/strong&gt; button
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6o4o9jnljgnkh5zwhhph.png" alt="CleanShot 2022-04-28 at 05.03.10@2x.png" width="800" height="991"&gt;
&lt;/li&gt;
&lt;li&gt;Add these name server records in your domain control panel (ex. Namecheap, GoDaddy). You might have different records than mine
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuq6v1nhrnciz8wl2yspj.png" alt="CleanShot 2022-04-28 at 05.09.00@2x.png" width="800" height="587"&gt;
&lt;/li&gt;
&lt;li&gt;Now to the good part. Click &lt;strong&gt;+ Add Record&lt;/strong&gt; button. Add your instance and click on tick icon to save. 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvxuiwuq18mk24kfdjuyn.png" alt="CleanShot 2022-04-28 at 05.13.07@2x.png" width="800" height="652"&gt;
&amp;gt; ⚠️ It may take few minutes to days to propagate DNS records. &lt;/li&gt;
&lt;li&gt;Now update the domain name in &lt;code&gt;siteurl&lt;/code&gt; and &lt;code&gt;home&lt;/code&gt; rows of &lt;code&gt;wp_options&lt;/code&gt; table.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Import Existing App
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Import Database
&lt;/h3&gt;

&lt;p&gt;You may have an existing database. You can import your database to the Lightsail instance. You can use a Database client app like TablePlus, MySQL Workbench, or even just the terminal. &lt;/p&gt;

&lt;p&gt;Your database credentials exist in &lt;code&gt;wp-config.php&lt;/code&gt; file. Connect to your instance via SSH and run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;stack/wordpress/wp-config.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use DB credentials to connect to DB, and replace &lt;code&gt;localhost&lt;/code&gt; with the instance's public IP. Connect to your DB via SSH (recommended) or add the &lt;code&gt;3306&lt;/code&gt; port here: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fniep7hurjfzwqvcbg2ef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fniep7hurjfzwqvcbg2ef.png" alt="CleanShot 2022-04-27 at 20.40.01@2x.png" width="800" height="662"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ Don't forget to change the domain name in &lt;code&gt;siteurl&lt;/code&gt; and &lt;code&gt;home&lt;/code&gt; rows of &lt;code&gt;wp_options&lt;/code&gt; table.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Import existing files
&lt;/h3&gt;

&lt;p&gt;Application folder located in &lt;code&gt;stack/wordpress&lt;/code&gt;. You can upload files in two ways: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SFTP Client: You can use SFTP clients like FileZilla or Transmit to upload files. Use the same SSH credential to upload files. &lt;/li&gt;
&lt;li&gt;Git: Connect to your instance via SSH. Install git by using this command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now pull your files from git using the git clone/pull command. &lt;/p&gt;

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

&lt;p&gt;This is just the first step toward deploying your WordPress app to Amazon Lightsail instance. There are lot more you can do. You are welcome to ask your questions, give me feedback, or any improvement request. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>lightsail</category>
      <category>wordpress</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
