Automating Nikola blog deployments with GitHub Actions
Building a Nikola static site is fast. Pushing to S3 is fast. But running them manually every time you publish a post is tedious. GitHub Actions automates both in a few minutes.
The setup we built
A workflow that:
1. Listens for pushes to main
2. Installs Nikola and dependencies
3. Builds the entire site
4. Pushes the output to S3
Every post goes live the moment you push.
Create limited AWS credentials
First, create an IAM user with S3-only access to your bucket. This follows the principle of least privilege:
aws iam create-user --user-name blog-deploy-user aws iam put-user-policy --user-name blog-deploy-user \ --policy-name s3-bucket-deploy \ --policy-document '{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:DeleteObject", "s3:GetObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::your-bucket", "arn:aws:s3:::your-bucket/*" ] } ] }' aws iam create-access-key --user-name blog-deploy-user
Save those credentials. You'll need them in a moment.
Add secrets to GitHub
In your repository settings, go to Secrets and variables → Actions.
Add the following secrets:
AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY
The workflow file
Create .github/workflows/deploy.yml:
name: Build and Deploy Blog on: push: branches: - main jobs: build_and_deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install Nikola run: pip install 'nikola[extras]' watchdog aiohttp - name: Build blog run: nikola build - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-west-2 - name: Deploy to S3 run: | aws s3 sync output/ s3://your-bucket/ \ --delete \ --exclude "*.md" \ --exclude ".git/*"
The --delete flag removes files from S3 that aren't in your build output (useful when renaming posts). The --exclude flags skip markdown source and git files.
A note on the official Nikola action
The getnikola/nikola-action is designed for GitHub Pages deployments (github_deploy), not S3. If you're using S3 (or another deployment method), a custom workflow like this is simpler and more transparent.
The action also had a subtle issue: running inside a Docker container, it couldn't find the repository due to git's safe.directory check (a security feature added in Git 2.36). The workaround was to create .gitconfig in the runner's home directory before the container ran, but at that point, using a direct Python install was cleaner. (See the related runner issue for details.)
Next steps
- Customize the S3 sync command for your bucket and path
- Add CloudFront cache invalidation if you use CloudFront as a CDN
- Add Slack or Discord notifications on deployment success or failure using GitHub Actions workflows
- Consider
--cache-controlheaders to bust caches on HTML files (e.g.,aws s3 cp ... --cache-control "max-age=300")
Deploy from the comfort of your editor. No manual builds, no SSH tunnels.
Comments
Comments powered by Disqus