
Minimal blog using Rails 8, designed to be easily self-hosted on AWS.
- Light/Dark mode
- Responsive Layout
- RSS/Atom
- Markdown and Code Highlighting
- Link Blog
- Drag and Drop image uploads for Pages and Posts
$ git clone https://github.com/capotej/abbey.git
$ cd abbey
You'll want a modern Ruby installed, at least 3.2.0
, as of writing I am using 3.4.1
. You can use rbenv and the ruby-build plugin to run rbenv install 3.4.1
.
$ bundle install
Open config/initializers/site_settings.rb
to change site-specific settings, such as Rails.application.config.site_name
.
$ rake db:setup
$ bin/dev
If everything went smoothly, you should see the blog running at http://127.0.0.1:3000
with some example content to get you started.
You can import posts by running:
$ rake "blog:import[/path/to/content]"
This will scan the given path for files ending in .markdown
and create a seed for each one in db/seeds
using information found in the front matter. You can then insert those imported seeds by running:
$ rake db:reset
Note: This will delete everything in the local database and re-seed using db/seeds/*
.
This deployment guide makes the following assumptions:
-
You have an AWS account and are familiar with IAM policies.
-
You use Tailscale with Tailscale SSH configured.
-
Your computer uses ARM/Apple Silicon.
-
You have a tool for loading
.env
files, like dotenvx.
- A working
docker
setup. On Mac, you can use Rancher Desktop.
This role will need to be able to attach EBS volumes as well create log groups and streams inside of CloudWatch.
For example:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Resource": [
"arn:aws:logs:*:*:log-group:*:*",
"arn:aws:logs:*:*:log-group:*:*:log-stream:*"
]
},
{
"Effect": "Allow",
"Action": [
"ec2:AttachVolume",
"ec2:DescribeVolumes"
],
"Resource": "*"
}
]
}
Create an EBS volume of the desired size, noting the Availibility Zone it gets created in.
You can use the Lifecycle Manager to create a default policy that backs up all EBS volumes going back 7 days.
Create an .env
file with the following variables set:
# Get this at https://login.tailscale.com/admin/settings/keys
# Note: These are one-time use, you'll need to generate a new one whenever you are provisioning a new instance
TAILSCALE_AUTH_KEY=tskey-auth-xxxxxxx
# The hostname on the instance will be set to this
# Note: This uses Tailscale HTTPS to generate certificates, so this hostname will be exposed in a public ledger: https://tailscale.com/kb/1153/enabling-https#machine-names-in-the-public-ledger
TAILSCALE_HOSTNAME=my-blog-host-1
# Your hostname + Tailnet name: https://tailscale.com/kb/1217/tailnet-name
TAILSCALE_FQDN=my-blog-host-1.tailxxxxx.ts.net
# The EBS volume-id created in Step 1
EBS_VOLUME_ID=vol-00000000
The following rake task generates a cloud-init
script for AWS & AWS Linux.
$ dotenvx run --quiet -- rake cloud_init:generate
This will do the following on a newly created instance:
- Sets hostname
- Sends System & Application logs to CloudWatch
- Attaches and mounts EBS volume, partitioning/formatting, if necessary
- Joins your Tailnet
- Sets up a Docker registry in Tailnet, using Tailscale HTTPS
Create an EC2 instance with the following settings:
- Image:
Amazon Linux 2023 AMI
- Architecture:
64-bit (Arm)
- Type:
t2.small
- Keypair:
Proceed without a key pair
(Tailscale SSH will be managing access) - Network & Subnet: Ensure subnet is in the same Availability Zone as the EBS Volume created in Step 2
- Firewall (security groups): Only allow
HTTP (80)
andHTTPS (443)
from anyIPv4
orIPv6
address - Storage: Default is fine,
8GiB gp3
Root Volume - Advanced Details / IAM Instance Profile: The role created in Step 1
- Advanced Details / User data: The output of Step 4 (on Mac you can add
| pbcopy
to get it in your clipboard)
Create an A Record
pointing to the public IPv4 address of the instance created in Step 5.
Open config/deploy.yml
and change the host:
to the domain used in Step 6:
proxy:
ssl: true
host: capotej.com
$ dotenvx run -- kamal setup
If you used rake "blog:import[/path/to/posts]"
above, this will create those posts at this time.
$ dotenvx run -- kamal app exec -i rake db:setup
$ dotenvx run -- kamal console
rails(production)> User.destroy_all
rails(production)> User.create!(email_address: "you@example.org", password: "s3cr3t", password_confirmation: "s3cr3t")
$ dotenvx run -- kamal logs
$ dotenvx run -- kamal console
$ dotenvx run -- kamal deploy
$ dotenvx run -- kamal app exec -i rake db:setup