<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged bucket</title>
<link>https://coderlegion.com/tag/bucket</link>
<description>Powered by Question2Answer</description>
<item>
<title>S3 Policies Explained: Bucket Policies vs IAM Policies vs ACLs</title>
<link>https://coderlegion.com/14663/s3-policies-explained-bucket-policies-vs-iam-policies-vs-acls</link>
<description>
&lt;p&gt;&lt;img src=&quot;https://blog.ratnesh-maurya.com/images/blog/Understanding-S3-and-S3-Policies.jpg&quot; alt=&quot;S3 Policies Explained: Bucket Policies vs IAM Policies vs ACLs&quot;&gt;&lt;/p&gt;&lt;p&gt;S3 has three overlapping access control systems &amp;mdash; bucket policies, IAM policies, and ACLs &amp;mdash; and the interaction between them confuses most people the first time. Here's how each one works, when to use which, and the JSON to copy for the most common scenarios.&lt;/p&gt;
&lt;h2 id=&quot;the-three-access-control-layers&quot;&gt;The three access control layers&lt;/h2&gt;
&lt;p&gt;Every S3 request is evaluated against all applicable policies. If any of them explicitly deny the request, it's denied. Otherwise, at least one policy must explicitly allow it.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;Attached to&lt;/th&gt;
&lt;th&gt;Written by&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bucket policies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The S3 bucket&lt;/td&gt;
&lt;td&gt;Bucket owner&lt;/td&gt;
&lt;td&gt;Cross-account access, public access, IP restrictions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IAM policies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;IAM users/roles/groups&lt;/td&gt;
&lt;td&gt;Account admin&lt;/td&gt;
&lt;td&gt;Controlling what your own users and services can do&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ACLs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Buckets or objects&lt;/td&gt;
&lt;td&gt;Object owner&lt;/td&gt;
&lt;td&gt;Legacy use only &amp;mdash; AWS recommends disabling these&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;The rule of thumb:&lt;/strong&gt; Use IAM policies for your own users, bucket policies for external access or bucket-wide rules, and ignore ACLs unless you're dealing with legacy configurations.&lt;/p&gt;
&lt;h2 id=&quot;anatomy-of-an-s3-policy&quot;&gt;Anatomy of an S3 policy&lt;/h2&gt;
&lt;p&gt;Every policy is a JSON document with these fields:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Version&lt;/strong&gt; &amp;mdash; always &lt;code&gt;&quot;2012-10-17&quot;&lt;/code&gt; (the current policy language version)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Statement&lt;/strong&gt; &amp;mdash; an array of permission rules, each containing:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Effect&lt;/strong&gt; &amp;mdash; &lt;code&gt;&quot;Allow&quot;&lt;/code&gt; or &lt;code&gt;&quot;Deny&quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Principal&lt;/strong&gt; &amp;mdash; who the rule applies to (&lt;code&gt;&quot;*&quot;&lt;/code&gt; for everyone, or a specific ARN)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Action&lt;/strong&gt; &amp;mdash; which S3 operations (&lt;code&gt;s3:GetObject&lt;/code&gt;, &lt;code&gt;s3:PutObject&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource&lt;/strong&gt; &amp;mdash; which bucket/objects (specified as an ARN)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Condition&lt;/strong&gt; (optional) &amp;mdash; extra constraints like IP range, encryption type, or request origin&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;common-policies-with-working-json&quot;&gt;Common policies with working JSON&lt;/h2&gt;
&lt;h3 id=&quot;public-read-only-access&quot;&gt;Public read-only access&lt;/h3&gt;
&lt;p&gt;Makes all objects in a bucket publicly readable. Use this for static website hosting or public assets:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-json&quot;&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;PublicReadGetObject&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Principal&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:GetObject&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;arn:aws:s3:::my-bucket/*&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows anyone to read objects but not list the bucket contents, upload, or delete. The &lt;code&gt;/*&lt;/code&gt; in the Resource means all objects inside the bucket.&lt;/p&gt;
&lt;h3 id=&quot;deny-uploads-without-encryption&quot;&gt;Deny uploads without encryption&lt;/h3&gt;
&lt;p&gt;Forces all uploaded objects to use server-side encryption. Note: this uses &lt;code&gt;Deny&lt;/code&gt; + a &lt;code&gt;StringNotEquals&lt;/code&gt; condition, not &lt;code&gt;Allow&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-json&quot;&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;DenyUnencryptedUploads&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Deny&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Principal&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:PutObject&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;arn:aws:s3:::my-bucket/*&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Condition&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;&quot;StringNotEquals&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;&quot;s3:x-amz-server-side-encryption&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;AES256&quot;&lt;/span&gt;
        &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the correct pattern. A common mistake is using &lt;code&gt;Allow&lt;/code&gt; with a &lt;code&gt;StringEquals&lt;/code&gt; condition &amp;mdash; that permits encrypted uploads but doesn't block unencrypted ones if another policy allows &lt;code&gt;s3:PutObject&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;scoped-access-for-a-specific-iam-user&quot;&gt;Scoped access for a specific IAM user&lt;/h3&gt;
&lt;p&gt;Grants a single user read and write access to a bucket:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-json&quot;&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;UserReadWrite&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Principal&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;&quot;AWS&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;arn:aws:iam::123456789012:user/deploy-bot&quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:GetObject&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:PutObject&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:DeleteObject&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;arn:aws:s3:::my-bucket/*&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;UserListBucket&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Principal&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;&quot;AWS&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;arn:aws:iam::123456789012:user/deploy-bot&quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:ListBucket&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;arn:aws:s3:::my-bucket&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the two statements: object-level actions use &lt;code&gt;my-bucket/*&lt;/code&gt; (objects inside), while &lt;code&gt;ListBucket&lt;/code&gt; uses &lt;code&gt;my-bucket&lt;/code&gt; (the bucket itself). Mixing these up is a common source of &quot;Access Denied&quot; errors.&lt;/p&gt;
&lt;h3 id=&quot;restrict-access-by-ip-range&quot;&gt;Restrict access by IP range&lt;/h3&gt;
&lt;p&gt;Allows access only from your office or VPN IP range:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-json&quot;&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;RestrictToOfficeIP&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Deny&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Principal&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:*&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;arn:aws:s3:::my-bucket&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;arn:aws:s3:::my-bucket/*&quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Condition&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;&quot;NotIpAddress&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;&quot;aws:SourceIp&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;203.0.113.0/24&quot;&lt;/span&gt;
        &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;bucket-policy-vs-iam-policy-when-to-use-which&quot;&gt;Bucket policy vs IAM policy: when to use which&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Grant another AWS account access to your bucket&lt;/td&gt;
&lt;td&gt;Bucket policy (cross-account)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Make a bucket publicly readable&lt;/td&gt;
&lt;td&gt;Bucket policy (Principal: &lt;code&gt;*&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control what your CI/CD pipeline can do&lt;/td&gt;
&lt;td&gt;IAM policy on the pipeline's IAM role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restrict access by IP or VPN&lt;/td&gt;
&lt;td&gt;Bucket policy with Condition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Give a Lambda function access to a bucket&lt;/td&gt;
&lt;td&gt;IAM policy on the Lambda execution role&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deny all public access organization-wide&lt;/td&gt;
&lt;td&gt;S3 Block Public Access (account-level setting)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In general: if the question is &quot;who can access this bucket?&quot;, use a bucket policy. If the question is &quot;what can this user/role do?&quot;, use an IAM policy.&lt;/p&gt;
&lt;h2 id=&quot;common-mistakes&quot;&gt;Common mistakes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Forgetting S3 Block Public Access.&lt;/strong&gt; Even if your bucket policy allows public reads, the account-level Block Public Access setting overrides it. Check this first when public access isn't working.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource ARN mismatch.&lt;/strong&gt; &lt;code&gt;s3:ListBucket&lt;/code&gt; needs the bucket ARN (&lt;code&gt;arn:aws:s3:::my-bucket&lt;/code&gt;), while &lt;code&gt;s3:GetObject&lt;/code&gt; needs the object ARN (&lt;code&gt;arn:aws:s3:::my-bucket/*&lt;/code&gt;). This trips up almost everyone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Using ACLs.&lt;/strong&gt; AWS recommends disabling ACLs on new buckets (S3 Object Ownership: &quot;Bucket owner enforced&quot;). Bucket policies and IAM policies cover every use case that ACLs used to handle, with better auditability.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Overly broad wildcards.&lt;/strong&gt; &lt;code&gt;&quot;Action&quot;: &quot;s3:*&quot;&lt;/code&gt; with &lt;code&gt;&quot;Resource&quot;: &quot;*&quot;&lt;/code&gt; is an admin-level policy. Scope both to the specific actions and bucket you need.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://awspolicygen.s3.amazonaws.com/policygen.html&quot;&gt;AWS Policy Generator&lt;/a&gt; can help you build policies interactively if you're not sure about the syntax.&lt;/p&gt;
</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/14663/s3-policies-explained-bucket-policies-vs-iam-policies-vs-acls</guid>
<pubDate>Tue, 21 Apr 2026 02:19:56 +0000</pubDate>
</item>
<item>
<title>Deploy a Nanoc Static Site to S3 with GitHub Actions</title>
<link>https://coderlegion.com/14660/deploy-a-nanoc-static-site-to-s3-with-github-actions</link>
<description>
&lt;p&gt;&lt;img src=&quot;https://blog.ratnesh-maurya.com/images/blog/Easily-Deploy-Your-Nanoc-Website-to-S3-with-GitHub-Actions.jpg&quot; alt=&quot;Deploy a Nanoc Static Site to S3 with GitHub Actions&quot;&gt;&lt;/p&gt;&lt;p&gt;Deploying a static site to S3 manually means running &lt;code&gt;aws s3 sync&lt;/code&gt; from your laptop every time you make a change. That gets old fast. GitHub Actions can automate the entire flow: push to &lt;code&gt;main&lt;/code&gt;, and the site builds and deploys itself.&lt;/p&gt;
&lt;p&gt;This guide walks through the full setup for a &lt;a rel=&quot;nofollow&quot; href=&quot;https://nanoc.app/&quot;&gt;Nanoc&lt;/a&gt; site, but the S3 + GitHub Actions pattern works for any static site generator.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/ratnesh-maurya/365-Days-of-DevOps/tree/main&quot;&gt;Source code on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-you-need-before-starting&quot;&gt;What you need before starting&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;AWS account&lt;/strong&gt; with an S3 bucket configured for static website hosting&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;IAM user&lt;/strong&gt; with scoped permissions (created in the steps below)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;GitHub repository&lt;/strong&gt; containing your Nanoc source code&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;step-1-configure-the-s3-bucket-policy&quot;&gt;Step 1: Configure the S3 bucket policy&lt;/h2&gt;
&lt;p&gt;Your bucket needs a policy that allows public read access to its contents. Apply this to the bucket in the S3 console under Permissions &amp;gt; Bucket Policy:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-json&quot;&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;2008-10-17&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Id&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;PolicyForPublicWebsiteContent&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;PublicReadGetObject&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Principal&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;&quot;AWS&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;*&quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:GetObject&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;arn:aws:s3:::docsite-github-action/*&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace &lt;code&gt;docsite-github-action&lt;/code&gt; with your actual bucket name.&lt;/p&gt;
&lt;h2 id=&quot;step-2-create-a-scoped-iam-user&quot;&gt;Step 2: Create a scoped IAM user&lt;/h2&gt;
&lt;p&gt;Create a dedicated IAM user for GitHub Actions &amp;mdash; don't use your root credentials. Attach this policy, which gives the minimum permissions needed for &lt;code&gt;s3 sync&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-json&quot;&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;AccessToGetBucketLocation&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:GetBucketLocation&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&quot;arn:aws:s3:::*&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;AccessToWebsiteBuckets&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:PutObject&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:PutObjectAcl&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:GetObject&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:ListBucket&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;s3:DeleteObject&quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;arn:aws:s3:::docsite-github-action&quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&quot;arn:aws:s3:::docsite-github-action/*&quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;step-3-add-aws-credentials-to-github-secrets&quot;&gt;Step 3: Add AWS credentials to GitHub Secrets&lt;/h2&gt;
&lt;p&gt;In your GitHub repository, go to &lt;strong&gt;Settings &amp;gt; Secrets and variables &amp;gt; Actions&lt;/strong&gt; and add two repository secrets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; &amp;mdash; your IAM user's access key&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt; &amp;mdash; the corresponding secret key&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are injected into the workflow at runtime. They never appear in logs.&lt;/p&gt;
&lt;h2 id=&quot;step-4-create-the-github-actions-workflow&quot;&gt;Step 4: Create the GitHub Actions workflow&lt;/h2&gt;
&lt;p&gt;Create &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt; in your repository:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Nanoc&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Compile&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Upload&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;S3&lt;/span&gt;

&lt;span class=&quot;hljs-attr&quot;&gt;on:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;push:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;branches:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;main&lt;/span&gt;

&lt;span class=&quot;hljs-attr&quot;&gt;jobs:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;deploy:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;runs-on:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class=&quot;hljs-attr&quot;&gt;steps:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Checkout&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Repository&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;up&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Ruby&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Nanoc&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;|
          sudo apt-get update
          sudo apt-get install -y ruby-full build-essential zlib1g-dev
          sudo gem install bundler
          sudo gem install nanoc
          sudo gem install adsf
          sudo gem install kramdown
&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Build&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Nanoc&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Website&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;|
          ls
          cd tutorial &amp;amp;&amp;amp; nanoc
&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Set&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;AWS&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;credentials&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;aws-actions/configure-aws-credentials@v1&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;aws-access-key-id:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;${{&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;secrets.AWS_ACCESS_KEY_ID&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;aws-secret-access-key:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;${{&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;secrets.AWS_SECRET_ACCESS_KEY&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;aws-region:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ap-south-1&lt;/span&gt;

      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Push&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;S3&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;aws&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;s3&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;sync&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;tutorial/output/&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;s3://docsite-github-action&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The workflow triggers on every push to &lt;code&gt;main&lt;/code&gt;. It installs Ruby and Nanoc, compiles the site, and syncs the output directory to your S3 bucket.&lt;/p&gt;
&lt;h2 id=&quot;alternatives-to-consider&quot;&gt;Alternatives to consider&lt;/h2&gt;
&lt;p&gt;This setup works well for simple static sites, but depending on your needs, other approaches might be a better fit:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;When to use it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S3 + CloudFront&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You need HTTPS and a CDN for global performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vercel / Netlify&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You want zero-config deploys with preview URLs for every PR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GitHub Pages&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You don't need AWS and want the simplest possible hosting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Amplify&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You want managed CI/CD with automatic branch deploys on AWS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;S3 alone doesn't give you HTTPS &amp;mdash; you'd need CloudFront in front of it for that. If HTTPS and preview deployments matter to you, Vercel or Netlify will save you time.&lt;/p&gt;
&lt;h2 id=&quot;common-pitfalls&quot;&gt;Common pitfalls&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Forgetting to enable static website hosting&lt;/strong&gt; on the S3 bucket &amp;mdash; without it, S3 serves files as downloads instead of web pages.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Overly broad IAM permissions&lt;/strong&gt; &amp;mdash; scope the policy to your specific bucket, not &lt;code&gt;s3:*&lt;/code&gt; on &lt;code&gt;*&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cache invalidation&lt;/strong&gt; &amp;mdash; &lt;code&gt;s3 sync&lt;/code&gt; updates files but doesn't invalidate CloudFront caches. If you add CloudFront later, add &lt;code&gt;aws cloudfront create-invalidation&lt;/code&gt; to the workflow.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Region mismatch&lt;/strong&gt; &amp;mdash; set &lt;code&gt;aws-region&lt;/code&gt; in the workflow to match your bucket's region.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The complete workflow YAML and policies are in the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/ratnesh-maurya/365-Days-of-DevOps/tree/main&quot;&gt;repo&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/14660/deploy-a-nanoc-static-site-to-s3-with-github-actions</guid>
<pubDate>Mon, 13 Apr 2026 22:47:12 +0000</pubDate>
</item>
</channel>
</rss>