This is a short guide on how to enable usage of backing up your Joplin notes to the cheap and powerful cloud service service S3 which AWS(Amazon Web Services) provides.

Assumptions

  • You have an AWS account
  • You have a basic understanding of Terraform and how to use it with AWS

The Terraform parts

Note about the code structure

As a habit from work I tend to keep resources related to a specific cloud service in a separate TF file for that given service. So things for AWS s3 goes into s3.tf while anything related to AWS IAM goes in an iam.tf file.

This is of course not necessary to do, but I find it easier to navigate and find specific resources this way.

Code

S3 - s3.tf

First we need to create an S3 bucket where our Joplin data will reside.

For security purposes we turn off any public access to the bucket and prevent the bucket from being destroyed by accident through Terraform.

Versioning on the bucket should not be needed as Joplin has note versioning built into itself.

resource "aws_s3_bucket" "joplin" {
  bucket_prefix   = "joplin"
  versioning {
    enabled = false
  }
  lifecycle {
    prevent_destroy = true
  }
}

resource "aws_s3_bucket_public_access_block" "joplin" {
  bucket                  = aws_s3_bucket.joplin.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

output "s3_bucket" {
  value = aws_s3_bucket.joplin.bucket
}

IAM - iam.tf

We need to create a user which can hold the access key and secret which Joplin will need to synchronise to the S3 bucket. The user will have a custom policy which gives it restrictive access to operations on the bucket we created.

The access key is generated here and it’s id used as an output, the secret part will be stored in the AWS parameter store rather than being outputted as raw text.

resource "aws_iam_user" "joplin" {
  name = "joplin"
}

data "aws_iam_policy_document" "joplin" {
  version = "2012-10-17"
  statement {
    effect = "Allow"
    actions = [ 
      "s3:Listbucket",
      "s3:GetBucketLocation",
      "s3:GetObject",
      "s3:DeleteObject",
      "s3:PutObject",
      "s3:DeleteObjectVersion"
    ]
    resources = [ 
      aws_s3_bucket.joplin.arn,
      "${aws_s3_bucket.joplin.arn}/*"
     ]
  }
}

resource "aws_iam_policy" "joplin" {
  name = "joplin"
  policy = data.aws_iam_policy_document.joplin.json
}

resource "aws_iam_policy_attachment" "joplin" {
  name = "joplin-attachment"
  policy_arn = aws_iam_policy.joplin.arn
  users = [ aws_iam_user.joplin.name ] 
}

resource "aws_iam_access_key" "joplin" {
  user = aws_iam_user.joplin.name
}

output "iam_access_key_id" {
  value = aws_iam_access_key.joplin.id
}

SSM - ssm.tf

To store the users access secret in a safe manner we will use AWS SSM. It’s cheap and reliable and helps us avoid flaunting secrets around in terminals when not absolutely necessary.

resource "aws_ssm_parameter" "joplin_access_secret" {
  name  = "/joplin/access_secret"
  type  = "SecureString"
  value = aws_iam_access_key.joplin.secret
}

Configuring Joplin

Open up Joplin, head into the options section in the menus and select Synchronisation.

For Synchronisation target you select AWS S3.

For AWS S3 bucket you enter the S3 bucket name from the Terraform outputs.

For AWS S3 URL you enter https://s3.amazonaws.com.

For AWS key you enter the IAM access key id from the Terraform outputs.

And lastly, for AWS secret you enter the secret which was stored in AWS SSM. The easiest way of obtaining the secret is to login into the AWS console and get it from the SSM service.

Now, hit the Check synchronisation configuration, It should return an answer akin to “Success! Synchronisation configuration appears to be correct." if everything is working as it should.