feat: set up basic blog structure
This commit is contained in:
parent
f4f421ac43
commit
8599464f2d
7 changed files with 1740 additions and 88 deletions
|
|
@ -6,26 +6,56 @@ const config: GatsbyConfig = {
|
||||||
siteUrl: `https://felschr.com`
|
siteUrl: `https://felschr.com`
|
||||||
},
|
},
|
||||||
graphqlTypegen: true,
|
graphqlTypegen: true,
|
||||||
plugins: ["gatsby-plugin-emotion", "gatsby-plugin-image", "gatsby-plugin-sitemap", {
|
plugins: [
|
||||||
resolve: 'gatsby-plugin-manifest',
|
"gatsby-plugin-emotion",
|
||||||
|
"gatsby-plugin-image",
|
||||||
|
"gatsby-plugin-react-helmet",
|
||||||
|
"gatsby-plugin-sitemap",
|
||||||
|
{
|
||||||
|
resolve: "gatsby-plugin-manifest",
|
||||||
options: {
|
options: {
|
||||||
"icon": "src/images/icon.png"
|
"icon": "src/images/icon.png"
|
||||||
}
|
}
|
||||||
}, "gatsby-plugin-mdx", "gatsby-plugin-sharp", "gatsby-transformer-sharp", {
|
|
||||||
resolve: 'gatsby-source-filesystem',
|
|
||||||
options: {
|
|
||||||
"name": "images",
|
|
||||||
"path": "./src/images/"
|
|
||||||
},
|
},
|
||||||
__key: "images"
|
{
|
||||||
}, {
|
resolve: "gatsby-plugin-mdx",
|
||||||
resolve: 'gatsby-source-filesystem',
|
|
||||||
options: {
|
options: {
|
||||||
"name": "pages",
|
extensions: [".mdx", ".md"],
|
||||||
"path": "./src/pages/"
|
gatsbyRemarkPlugins: [
|
||||||
|
{
|
||||||
|
resolve: "gatsby-remark-highlight-code",
|
||||||
|
options: {
|
||||||
|
terminal: "none"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
__key: "pages"
|
],
|
||||||
}]
|
},
|
||||||
|
},
|
||||||
|
"gatsby-plugin-mdx-frontmatter",
|
||||||
|
"gatsby-plugin-sharp",
|
||||||
|
"gatsby-transformer-sharp",
|
||||||
|
{
|
||||||
|
resolve: "gatsby-source-filesystem",
|
||||||
|
options: {
|
||||||
|
name: "images",
|
||||||
|
path: "./src/images/"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
resolve: "gatsby-source-filesystem",
|
||||||
|
options: {
|
||||||
|
name: "pages",
|
||||||
|
path: "./src/pages/"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
resolve: "gatsby-source-filesystem",
|
||||||
|
options: {
|
||||||
|
name: "posts",
|
||||||
|
path: "./src/posts/",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|
|
||||||
1594
package-lock.json
generated
1594
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -16,6 +16,7 @@
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@deckdeckgo/highlight-code": "^3.6.0",
|
||||||
"@emotion/react": "^11.10.0",
|
"@emotion/react": "^11.10.0",
|
||||||
"@emotion/styled": "^11.10.0",
|
"@emotion/styled": "^11.10.0",
|
||||||
"@mdx-js/mdx": "^1.6.22",
|
"@mdx-js/mdx": "^1.6.22",
|
||||||
|
|
@ -25,9 +26,13 @@
|
||||||
"gatsby-plugin-image": "^2.19.0",
|
"gatsby-plugin-image": "^2.19.0",
|
||||||
"gatsby-plugin-manifest": "^4.19.0",
|
"gatsby-plugin-manifest": "^4.19.0",
|
||||||
"gatsby-plugin-mdx": "^3.19.0",
|
"gatsby-plugin-mdx": "^3.19.0",
|
||||||
|
"gatsby-plugin-mdx-frontmatter": "^0.0.4",
|
||||||
|
"gatsby-plugin-react-helmet": "^5.19.0",
|
||||||
"gatsby-plugin-sharp": "^4.19.0",
|
"gatsby-plugin-sharp": "^4.19.0",
|
||||||
"gatsby-plugin-sitemap": "^5.19.0",
|
"gatsby-plugin-sitemap": "^5.19.0",
|
||||||
|
"gatsby-remark-highlight-code": "^3.2.0",
|
||||||
"gatsby-source-filesystem": "^4.19.0",
|
"gatsby-source-filesystem": "^4.19.0",
|
||||||
|
"gatsby-transformer-remark": "^5.19.0",
|
||||||
"gatsby-transformer-sharp": "^4.19.0",
|
"gatsby-transformer-sharp": "^4.19.0",
|
||||||
"react": "^18.1.0",
|
"react": "^18.1.0",
|
||||||
"react-dom": "^18.1.0"
|
"react-dom": "^18.1.0"
|
||||||
|
|
|
||||||
17
src/components/organisms/Layout.tsx
Normal file
17
src/components/organisms/Layout.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
type LayoutProps = {
|
||||||
|
pageTitle: string
|
||||||
|
children: React.ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const Layout = ({ pageTitle, children }: LayoutProps) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>{pageTitle}</h1>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Layout
|
||||||
35
src/pages/blog/index.tsx
Normal file
35
src/pages/blog/index.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import { graphql, PageProps } from "gatsby"
|
||||||
|
import Layout from "../../components/organisms/Layout"
|
||||||
|
|
||||||
|
const Blog = ({ data: { allMdx } }: PageProps<Queries.BlogQuery>) => {
|
||||||
|
console.log("allMdx", allMdx)
|
||||||
|
return (
|
||||||
|
<Layout pageTitle="Blog">
|
||||||
|
{allMdx.edges.map(({ node: post }) => (
|
||||||
|
<a href={`//${location.host}/blog/${post.slug}`}>
|
||||||
|
<h2>{post.frontmatter?.title}</h2>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const query = graphql`
|
||||||
|
query Blog {
|
||||||
|
allMdx {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
slug
|
||||||
|
frontmatter {
|
||||||
|
title
|
||||||
|
published
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default Blog
|
||||||
33
src/pages/blog/{mdx.slug}.tsx
Normal file
33
src/pages/blog/{mdx.slug}.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import { graphql, PageProps } from "gatsby"
|
||||||
|
import { MDXRenderer } from "gatsby-plugin-mdx"
|
||||||
|
import Layout from "../../components/organisms/Layout"
|
||||||
|
|
||||||
|
import { defineCustomElements as deckDeckGoHighlightElement } from "@deckdeckgo/highlight-code/dist/loader";
|
||||||
|
deckDeckGoHighlightElement();
|
||||||
|
|
||||||
|
const BlogPost = ({ data: { mdx } }: PageProps<Queries.BlogPostQuery>) => {
|
||||||
|
return (
|
||||||
|
<Layout pageTitle={mdx?.frontmatter?.title ?? ""}>
|
||||||
|
<MDXRenderer>
|
||||||
|
{mdx?.body ?? ""}
|
||||||
|
</MDXRenderer>
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const query = graphql`
|
||||||
|
query BlogPost($id: String) {
|
||||||
|
mdx(id: {eq: $id}) {
|
||||||
|
id
|
||||||
|
body
|
||||||
|
frontmatter {
|
||||||
|
title
|
||||||
|
published
|
||||||
|
updated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export default BlogPost
|
||||||
76
src/posts/nixos-restic-backups.mdx
Normal file
76
src/posts/nixos-restic-backups.mdx
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
---
|
||||||
|
title: Optimised Backups on NixOS with restic & fd
|
||||||
|
published: 2022-08-01
|
||||||
|
updated: 2022-08-01
|
||||||
|
---
|
||||||
|
|
||||||
|
#### UPDATE 2022-07-04
|
||||||
|
|
||||||
|
After using this approach for a while I noticed a couple of downsides. The first most obvious is that all paths created by the [`dynamicFilesFrom`](https://search.nixos.org/options?channel=unstable&show=services.restic.backups.%3Cname%3E.dynamicFilesFrom&from=0&size=15&sort=relevance&type=packages&query=services.restic.backups.%3Cname%3E.dynamicFilesFrom) clutter the logs. More problematic is that restic's incremental backups only work properly when backup paths don't change ([#2246](https://github.com/restic/restic/issues/2246)). This causes restic to rescan all files on every run, and the prune job won't clean up all past backups.
|
||||||
|
Thus, my current recommendation is using [`paths`](https://search.nixos.org/options?channel=unstable&show=services.restic.backups.%3Cname%3E.paths&from=0&size=15&sort=relevance&type=packages&query=services.restic.backups.%3Cname%3E.paths) and excluding files by using `--exclude-file` in [`extraBackupArgs`](https://search.nixos.org/options?channel=unstable&show=services.restic.backups.%3Cname%3E.extraBackupArgs&from=0&size=15&sort=relevance&type=packages&query=services.restic.backups.%3Cname%3E.extraBackupArgs).
|
||||||
|
I'll update this post later with full instructions & a way to still backups source directories while respecting `.gitignore` files.
|
||||||
|
|
||||||
|
#### Introduction
|
||||||
|
|
||||||
|
I'd just like to present a simple restic backup configuration I've been using that allows excluding a lot of unnecessary files via ignore patterns & by respecting `.gitignore`, thus shrinking backup sizes.
|
||||||
|
|
||||||
|
While the NixOS options for restic allow specifying paths to include via [`services.restic.backups.<name>.paths`](https://search.nixos.org/options?channel=unstable&show=services.restic.backups.%3Cname%3E.paths&from=0&size=15&sort=relevance&type=packages&query=services.restic.backups.%3Cname%3E.paths) there is no native option to add exclusion patterns.
|
||||||
|
However, we can use [`services.restic.backups.<name>.dynamicFilesFrom`](https://search.nixos.org/options?channel=unstable&show=services.restic.backups.%3Cname%3E.dynamicFilesFrom&from=0&size=15&sort=relevance&type=packages&query=services.restic.backups.%3Cname%3E.dynamicFilesFrom) which expects a script that produces a list of paths to back up. This allows us to use other tools to create more granular inclusions.
|
||||||
|
|
||||||
|
#### Path matching
|
||||||
|
|
||||||
|
I've opted to use [`fd`](https://github.com/sharkdp/fd) in my configuration to generate the inclusion list. I've previously tried using `rg --files` but it doesn't match empty directories which can become an issue with services that expect certain paths to exist (PostgreSQL is one such example).
|
||||||
|
|
||||||
|
By default, `fd` ignores hidden files & respects various ignore files (`.gitignore`, `.ignore` & `.fdignore`).
|
||||||
|
We definitely want to include hidden files in backups, so we'll use `fd`'s `--hidden` argument.
|
||||||
|
Respecting ignore files on the other hand can be very useful to exclude caches & artefacts which can easily be reproduced. If you don't want this behaviour, it can be disabled with `--no-ignore`.
|
||||||
|
|
||||||
|
#### Excluding
|
||||||
|
|
||||||
|
Excluding paths from our backups can be very useful when we know the data is reproducible. E.g., if the target system runs podman containers that are all declared via NixOS options we don't really need most of the data in `/var/lib/containers`. When using volumes, though, `/var/lib/containers/storage/volumes` should be backed up.
|
||||||
|
|
||||||
|
#### Full solution
|
||||||
|
|
||||||
|
This is a simplified version of what I'm using in my desktop configuration. Hopefully some of these ignore patterns will fit for your use case.
|
||||||
|
|
||||||
|
Note the use of `sed` to escape `[` & `]`, restic would otherwise complain about these paths.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
services.restic.backups.full = let
|
||||||
|
paths = [ "/etc/nixos" "/var/lib/" "/home" ];
|
||||||
|
ignorePatterns = [
|
||||||
|
"/var/lib/systemd"
|
||||||
|
"/var/lib/containers"
|
||||||
|
"/var/lib/lxcfs"
|
||||||
|
"/var/lib/docker"
|
||||||
|
"/var/lib/flatpak"
|
||||||
|
"/home/*/.local/share/Trash"
|
||||||
|
"/home/*/.cache"
|
||||||
|
"/home/*/Downloads"
|
||||||
|
"/home/*/.npm"
|
||||||
|
"/home/*/Games"
|
||||||
|
"/home/*/.steam"
|
||||||
|
"/home/*/.local/share/containers"
|
||||||
|
"/home/*/.local/share/Steam"
|
||||||
|
"/home/*/.local/share/lutris"
|
||||||
|
"**/.git"
|
||||||
|
];
|
||||||
|
in {
|
||||||
|
initialize = true;
|
||||||
|
repository = "SOME-REPO";
|
||||||
|
timeConfig.OnCalendar = "daily";
|
||||||
|
dynamicFilesFrom = let
|
||||||
|
paths_ = foldl (a: b: "${a} ${b}") "" paths;
|
||||||
|
ignoreFile = builtins.toFile "ignore"
|
||||||
|
(foldl (a: b: a + "\n" + b) "" ignorePatterns);
|
||||||
|
in ''
|
||||||
|
${pkgs.fd}/bin/fd \
|
||||||
|
--hidden \
|
||||||
|
--ignore-file ${ignoreFile} \
|
||||||
|
. ${paths_} \
|
||||||
|
| sed "s/\[/\\\[/" | sed "s/\]/\\\]/"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
My full restic config can be found here: https://gitlab.com/felschr/nixos-config/tree/main/services/restic
|
||||||
Loading…
Add table
Add a link
Reference in a new issue