about summary refs log tree commit diff
path: root/users
diff options
context:
space:
mode:
authorFranck Cuny <franck@fcuny.net>2022-05-23 17:43:02 -0700
committerFranck Cuny <franck@fcuny.net>2022-05-23 17:43:02 -0700
commit14a5a6d480f0c2d71b3f43b272e07c5ce769ad83 (patch)
tree33c9de5c9d5d2da8d0dc16302dfb9500717a490b /users
parentfeat(tahoe): enable sourcegraph (diff)
parentsign the drone configuration (diff)
downloadworld-14a5a6d480f0c2d71b3f43b272e07c5ce769ad83.tar.gz
Merge remote-tracking branch 'blog/main' into fcuny/migrate-monorepo-blog
Diffstat (limited to '')
-rw-r--r--users/fcuny/blog/.drone.yml23
-rw-r--r--users/fcuny/blog/.envrc1
-rw-r--r--users/fcuny/blog/.gitignore3
-rw-r--r--users/fcuny/blog/Dockerfile7
-rw-r--r--users/fcuny/blog/README.org8
-rw-r--r--users/fcuny/blog/archetypes/default.md5
-rw-r--r--users/fcuny/blog/config.toml46
-rw-r--r--users/fcuny/blog/content/blog/git-link-and-sourcegraph.org49
-rw-r--r--users/fcuny/blog/content/blog/google-doc-failure.org56
-rw-r--r--users/fcuny/blog/content/blog/leaving-twitter.org10
-rw-r--r--users/fcuny/blog/content/blog/tailscale-docker-https.org121
-rw-r--r--users/fcuny/blog/flake.lock42
-rw-r--r--users/fcuny/blog/flake.nix46
-rw-r--r--users/fcuny/blog/fly.toml40
-rw-r--r--users/fcuny/blog/layouts/_default/baseof.html11
-rw-r--r--users/fcuny/blog/layouts/_default/list.html10
-rw-r--r--users/fcuny/blog/layouts/_default/single.html54
-rw-r--r--users/fcuny/blog/layouts/index.atom.xml24
-rw-r--r--users/fcuny/blog/layouts/index.html20
-rw-r--r--users/fcuny/blog/layouts/partials/footer.html32
-rw-r--r--users/fcuny/blog/layouts/partials/head.html18
-rw-r--r--users/fcuny/blog/layouts/partials/header.html10
-rw-r--r--users/fcuny/blog/layouts/partials/postlist.html12
-rw-r--r--users/fcuny/blog/layouts/taxonomy/tag.html6
-rwxr-xr-xusers/fcuny/blog/scripts/deploy.sh14
-rw-r--r--users/fcuny/blog/static/CNAME1
-rw-r--r--users/fcuny/blog/static/css/custom.css201
-rw-r--r--users/fcuny/blog/static/humans.txt10
-rw-r--r--users/fcuny/blog/static/profile.jpgbin0 -> 895450 bytes
-rw-r--r--users/fcuny/blog/static/resume.html209
-rw-r--r--users/fcuny/blog/static/resume.pdfbin0 -> 46970 bytes
-rw-r--r--users/fcuny/blog/static/ssh.pub.sig2
32 files changed, 1091 insertions, 0 deletions
diff --git a/users/fcuny/blog/.drone.yml b/users/fcuny/blog/.drone.yml
new file mode 100644
index 0000000..e312fb4
--- /dev/null
+++ b/users/fcuny/blog/.drone.yml
@@ -0,0 +1,23 @@
+---
+kind: pipeline
+type: exec
+name: default
+
+trigger:
+  event:
+    - push
+  branch:
+    - main
+
+steps:
+  - name: deploy
+    environment:
+      FLY_API_TOKEN:
+        from_secret: FLY_API_TOKEN
+    commands:
+      - nix run .#deploy
+---
+kind: signature
+hmac: 2fbe0f7be54296d1d2aa91d096b1a4e31cfa15104b2284fea15f0865e7cb3545
+
+...
diff --git a/users/fcuny/blog/.envrc b/users/fcuny/blog/.envrc
new file mode 100644
index 0000000..3550a30
--- /dev/null
+++ b/users/fcuny/blog/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/users/fcuny/blog/.gitignore b/users/fcuny/blog/.gitignore
new file mode 100644
index 0000000..8ec0a90
--- /dev/null
+++ b/users/fcuny/blog/.gitignore
@@ -0,0 +1,3 @@
+/docs/
+/.hugo_build.lock
+/result
diff --git a/users/fcuny/blog/Dockerfile b/users/fcuny/blog/Dockerfile
new file mode 100644
index 0000000..64c0f97
--- /dev/null
+++ b/users/fcuny/blog/Dockerfile
@@ -0,0 +1,7 @@
+FROM klakegg/hugo:0.91.2-ext-alpine-onbuild AS hugo
+
+FROM pierrezemb/gostatic
+
+COPY --from=hugo /target /srv/http/
+
+CMD ["-port", "8080" , "-https-promote"]
diff --git a/users/fcuny/blog/README.org b/users/fcuny/blog/README.org
new file mode 100644
index 0000000..0158727
--- /dev/null
+++ b/users/fcuny/blog/README.org
@@ -0,0 +1,8 @@
+#+TITLE: fcuny.net
+
+My personal blog.
+
+* Run
+The dependencies are managed with nix. Running =nix run= will start the hugo server.
+* Deploy
+You can deploy the site by running [[file:scripts/deploy.sh][scripts/deploy.sh]]. There's also a [[file:.drone.yml][.drone.yml]] configuration to trigger a build and deploy with drone.
diff --git a/users/fcuny/blog/archetypes/default.md b/users/fcuny/blog/archetypes/default.md
new file mode 100644
index 0000000..7ce2f1a
--- /dev/null
+++ b/users/fcuny/blog/archetypes/default.md
@@ -0,0 +1,5 @@
+---
+title: "{{ replace .Name "-" " " | title }}"
+date: {{ .Date }}
+---
+
diff --git a/users/fcuny/blog/config.toml b/users/fcuny/blog/config.toml
new file mode 100644
index 0000000..8cb0c62
--- /dev/null
+++ b/users/fcuny/blog/config.toml
@@ -0,0 +1,46 @@
+baseURL = "https://fcuny.net/"
+languageCode = "en-us"
+title = "Franck's rambling"
+publishDir = "docs"
+enableGitInfo = true
+
+[params]
+  homeText = "A collection of posts"
+
+[author]
+  name = "Franck Cuny"
+  email = "franck@fcuny.net"
+
+[taxonomies]
+  tag = "tags"
+
+[permalinks]
+  blog = "/blog/:slug/"
+  tags = "/tags/:slug/"
+
+[menu]
+
+[markup]
+  [markup.highlight]
+    anchorLineNos = false
+    codeFences = false
+    guessSyntax = false
+    hl_Lines = ""
+    lineAnchors = ""
+    lineNoStart = 1
+    lineNos = false
+    lineNumbersInTable = false
+    noClasses = true
+    style = "manni"
+    tabWidth = 4
+
+[mediaTypes."application/atom"]
+  suffixes = ["xml"]
+
+[outputFormats.Atom]
+  mediaType = "application/atom"
+  baseName = "feed"
+  isPlainText = false
+
+[outputs]
+  home = [ "HTML", "Atom" ]
diff --git a/users/fcuny/blog/content/blog/git-link-and-sourcegraph.org b/users/fcuny/blog/content/blog/git-link-and-sourcegraph.org
new file mode 100644
index 0000000..3ab5f4b
--- /dev/null
+++ b/users/fcuny/blog/content/blog/git-link-and-sourcegraph.org
@@ -0,0 +1,49 @@
+#+TITLE: emacs' git-link and sourcegraph
+#+TAGS[]: emacs git
+#+DATE: <2021-08-24 Tue>
+
+I use [[https://sourcegraph.com/][sourcegraph]] for searching code, and I sometimes need to share a link to the source code I'm looking at in a buffer. For this, the package [[https://github.com/sshaw/git-link][=git-link=]] is great.
+
+To integrate sourcegraph and =git-link=, the [[https://github.com/sshaw/git-link#sourcegraph][documentation]] recommends adding a remote entry named =sourcegraph= in the repository, like this:
+
+#+begin_src sh
+git remote add sourcegraph https://sourcegraph.com/github.com/sshaw/copy-as-format
+#+end_src
+
+The next time you run =M-x git-link= in a buffer, it will use the URL associated with that remote. That's works great, except that now you need to add this for every repository. Instead, for my usage, I came up with the following solution:
+
+#+begin_src elisp
+(use-package git-link
+  :ensure t
+  :after magit
+  :bind (("C-c g l" . git-link)
+         ("C-c g a" . git-link-commit))
+  :config
+  (defun fcuny/get-sg-remote-from-hostname (hostname)
+    (format "sourcegraph.<$domain>.<$tld>/%s" hostname))
+
+  (defun fcuny/git-link-work-sourcegraph (hostname dirname filename _branch commit start end)
+    ;;; For a given repository, build the proper link for sourcegraph.
+    ;;; Use the default branch of the repository instead of the
+    ;;; current one (we might be on a feature branch that is not
+    ;;; available on the remote).
+    (require 'magit-branch)
+    (let ((sg-base-url (fcuny/get-sg-remote-from-hostname hostname))
+          (main-branch (magit-main-branch)))
+      (git-link-sourcegraph sg-base-url dirname filename main-branch commit start end)))
+
+  (defun fcuny/git-link-commit-work-sourcegraph (hostname dirname commit)
+    (let ((sg-base-url (fcuny/get-sg-remote-from-hostname hostname)))
+      (git-link-commit-sourcegraph sg-base-url dirname commit)))
+
+  (add-to-list 'git-link-remote-alist '("twitter" fcuny/git-link-work-sourcegraph))
+  (add-to-list 'git-link-commit-remote-alist '("twitter" fcuny/git-link-commit-work-sourcegraph))
+
+  (setq git-link-open-in-browser 't))
+#+end_src
+
+We use different domains to host various git repositories at work (e.g. =git.$work=, =gitfoo.$work=, etc). Each of them map to a different URI for sourcegraph (e.g. =sourcegraph.$work/gitfoo=).
+
+=git-link-commit-remote-alist= is an [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Association-Lists.html][association list]] that takes a regular expression and a function. The custom function receives the hostname for the remote repository, which is then used to generate the URI for our sourcegraph instance. I then call =git-link-sourcegraph= replacing the hostname with the URI for sourcegraph.
+
+Now I can run =M-x git-link= in any repository where the host for the origin git repository matches =twitter= without having to setup the custom remote first.
diff --git a/users/fcuny/blog/content/blog/google-doc-failure.org b/users/fcuny/blog/content/blog/google-doc-failure.org
new file mode 100644
index 0000000..b4d449d
--- /dev/null
+++ b/users/fcuny/blog/content/blog/google-doc-failure.org
@@ -0,0 +1,56 @@
+#+TITLE: Google Doc Failures
+#+TAGS[]: documentation process
+#+DATE: <2021-04-11 Sun>
+
+In most use cases, Google Doc is an effective tool to create "write once, read never" documents.
+
+* Convenience
+Google Doc (GDoc from now on) is the most common way of writing and sharing documents at my current job. It's very easy to start a new document, even more since we can now point our browser to https://doc.new and start typing right away.
+
+Like most of my co-workers, I use it frequently during the day. Some of these documents are draft for some communication that I want others to review before I share with a broader audience; it can be a [[https://en.wikipedia.org/wiki/Request_for_Comments][Request For Comments]] for a project; meeting notes for others to read; information that I need to capture during an incident or a debugging session; interviews notes; etc.
+
+I would not be surprised if the teams I work closely with generate 50 new documents each week.
+* ETOOMANYTABS
+I have a tendency of having hundreds of open tabs in my browser during the week. A majority of these tabs are GDocs, and I think this is one of the true failure of the product. Why do I have so many tabs ? There's mainly two reasons.
+
+The first reason is a problem with Chrome's UX itself: it happily let me open the same URL as many times as I want in as many tabs, instead of sending me to the already opened tab if the document is loaded. It's not uncommon that I find the same document opened in 5 different tabs.
+
+The second reason, and it's the most important one, I know that if I need to read or comment on a doc and I close the tab, I'll likely never find that document again, or will completely forget about it.
+* Discoverability
+In 'the old days', you'd start a new document in Word or LibreOffice, and as you hit "save" for the first time, you've two decisions to make: how am I going to name that file, and where am I going to save it on disk.
+
+With GDoc these questions don't have to be answered, you don't have to name the file, and it does not matter where it lives. I've likely hundreds of docs named 'untitled' in my "drive". I also don't have to think about where they will live, because they are saved automatically for me. I'm sure there's hundreds of studies that show that these two simple steps are actually complex for many users and creates useless friction (in which folder do I store it; should I organize the docuemnts by team, years, projects; do I name it with the date and the current project; etc.).
+
+GDoc being a Google product, it seems pretty obvious that they would come up with a better solution: let's not organize in a strict hierarchy these files, and let's instead search for them.
+
+Unfortunately, GDoc's search is really poor (and I'm being kind). By default most of us start by looking for some words we know are in the doc, maybe even in the title. But when working on a multiple projects that are related to the same technology, you suddenly get hundreds of documents matching your query. It's unclear how the returned set is ordered (by date ? by author ? by some scoring that is invisible to me ?).
+
+You can also search by owners, but here is another annoying bit: I think about owner as author, so I usually type =author:foo= before realizing it does not work. And that implies you already know who's the owner of the document. In the case of TDDs (Technical Design Document), I might know which team is behind it, but rarely who's the actual author.
+
+I could search for the title, but I rarely remember or know the name of the document I'm looking for. I could also be looking by keywords, but when working on a project with tens of related documents, you have to open all the returned docs to see which one is the correct one.
+
+And then what about new members joining your the team ? They don't know which docs exist, who wrote them, and how they are named. They end up searching and hoping that something good will be returned.
+* Workflows
+More and more we create workflows around these documents: some of the docs are TDDs that are going through reviews; others are decision documents that require input from multiple teams and are pending approval; others are road map documents that also go through some review process.
+
+As a result we create templates for all kind of documents, with usually something like "draft → reviews → approved/rejected" at the top. We expect the owner of the doc to mark in bold what's the status of the doc to help the reader understand in what state the document is. It's difficult to keep track of open actions and comments. Yes, there's a way to get a list of all of them, but it's not in an obvious place.
+
+As a result, some engineers in my team built an external dashboard with swim lanes which captures the state of a document. We add new document with their URLs, add who are the reviewers, and we move the doc between the lanes. Now we have to operate a service and a database to keep track of the status of documents in GDoc.
+* Alternatives
+When it comes to technical document, I find that [[https://caitiem.com/2020/03/29/design-docs-markdown-and-git/][approach]] much more interesting. Some open source projects have adopted a similar workflow ([[https://github.com/kubernetes/enhancements/tree/master/keps][Kubernetes]], [[https://github.com/golang/proposal][Go]]).
+
+A new document starts its life as a text file (using what ever markup language your team/company prefers). The document is submitted for review, and the people who need to be consulted are added as reviewers. They can now comment on the document, the author can address them, mark them as resolved. It's clear in which state the document is: it's either in review, committed, or rejected. With this approach you also end up with a clear history, as time moves on you can amend the document by submitting a change, and the change goes through the same process.
+
+New comers will find the document in the repository, and if they want to see the conversation they can open the review associated with the original change. They can also see how the document evolved over time. It's also easy to publish these documents on an internal website, using a static site generator for example.
+
+One of the thing that I think are critical, is that all of that is done using the tools the engineers are already using for their day to day job: a text editor, a version control system, a code review tool.
+
+There's obviously challenges with this approach too:
++ *it's more heavy handed*: not every one likes to write in a text editor using a markup language. It can requires some time to learn or get used to the syntax
++ *it's harder to integrate schema / visuals*: but having them checked in in the repository also improves the discoverability
+
+It's also true that no all documents suffer the same challenges for discoverability:
++ meeting notes are usually linked to meeting invites (however if you were not part of the meeting, you end up with the same challenges to discover them)
++ drafts for communications are usually not relevant once the communication has been sent
++ interview notes are usually transferred to some tools for HR when the feedback is submitted
+
diff --git a/users/fcuny/blog/content/blog/leaving-twitter.org b/users/fcuny/blog/content/blog/leaving-twitter.org
new file mode 100644
index 0000000..9bc5027
--- /dev/null
+++ b/users/fcuny/blog/content/blog/leaving-twitter.org
@@ -0,0 +1,10 @@
+#+TITLE: Leaving Twitter
+#+DATE: <2022-01-15 Sat>
+
+January 7th 2022 was my last day at Twitter, after more than 7 years at the company.
+
+The first few years I worked as an SRE in the core-storage team, with the PUB/SUB and key-value store teams.
+
+I spend the last four years working with the Compute team, both maintaining and operating our (very large) Aurora/Mesos clusters, and also working on the adoption of kubernetes, both for our data centers and for the cloud. Working with Compute was extremely fulfilling to me, as I worked closely with our hardware engineering and kernel/operating system teams.
+
+During these 7 years, I was constantly pushed by my coworkers to grow, to step up to new challenges, and I learned a tremendous amount about running large scale distributed systems. I'm extremely glad for that experience, it was by far the most interesting and challenging job I've ever had so far.
diff --git a/users/fcuny/blog/content/blog/tailscale-docker-https.org b/users/fcuny/blog/content/blog/tailscale-docker-https.org
new file mode 100644
index 0000000..14e4cf1
--- /dev/null
+++ b/users/fcuny/blog/content/blog/tailscale-docker-https.org
@@ -0,0 +1,121 @@
+#+TITLE: Tailscale, Docker and HTTPS
+#+TAGS[]: docker tailscale traefik
+#+DATE: <2021-12-29 Wed>
+
+I run a number of services in my home network. For the majority of these services, I don't want to make them available on the internet, I want to only be able to access them when I'm on my home network. However, sometimes I'm not at home and I still want to access them. So far I've been using plain [[https://www.wireguard.com/][wireguard]] to achieve this. While the initial configuration for wireguard is pretty simple, it starts to be a bit more cumbersome as I add more hosts/containers. It's also not easy to share keys with other folks if I want to give access to some of the machines or services. For that reason I decided to give a look at [[https://tailscale.com/][tailscale]].
+
+There's already a lot of articles about tailscale and how to use and configure it. Their [[https://tailscale.com/kb/][documentation]] is also pretty good, so I won't cover the initial setup.
+
+As stated above, I want to access some of my services that are running as docker containers from anywhere. For web services, I want to use them through HTTPS, with a valid certificate, and without having to remember on which port the service it's listening. I also don't want to setup a PKI in my home lab for that (and I'm also not interested in configuring split DNS), and instead I prefer to use [[https://letsencrypt.org/][let's encrypt]] with a proper subdomain that is unique for each service.
+
+The [[https://tailscale.com/kb/1054/dns/][tailscale documentation]] has two suggestions for this:
+- use their magicDNS feature / split DNS
+- setup a subdomain on a public domain
+
+Since I already have a public domain that I use for my home network, I decided to go with the second option (I'm also uncertain how to achieve my goal using magicDNS without running tailscale inside the container).
+
+The public domain I'm using is managed through [[https://cloud.google.com/dns/docs/tutorials/create-domain-tutorial][Google Cloud Domain]]. I create a new record for the services I want to run (for example, ~dash~ for my instance of grafana), using the IP address from the tailscale node the service runs on (e.g. 100.83.51.12).
+
+For routing the traffic I use [[https://traefik.io/][traefik]]. The configuration for traefik looks like this:
+#+begin_src yaml
+global:
+  sendAnonymousUsage: false
+providers:
+  docker:
+    exposedByDefault: false
+entryPoints:
+  http:
+    address: ":80"
+  https:
+    address: ":443"
+certificatesResolvers:
+  dash:
+    acme:
+      email: franck@fcuny.net
+      storage: acme.json
+      dnsChallenge:
+        provider: gcloud
+#+end_src
+
+The important bit here is the ~certificatesResolvers~ part. I'll be using the [[https://doc.traefik.io/traefik/user-guides/docker-compose/acme-dns/][dnsChallenge]] instead of the [[https://doc.traefik.io/traefik/user-guides/docker-compose/acme-http/][httpChallenge]] to obtain the certificate from let's encrypt. For this to work, I need to specify the ~provider~ to be [[https://go-acme.github.io/lego/dns/gcloud/][gcloud]]. I'll also need a service account (see [[https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application][this doc]] to create it). I run ~traefik~ in a docker container, and the ~systemd~ unit file is below. The required bits for using the ~dnsChallenge~ with ~gcloud~ are:
+- the environment variable ~GCP_SERVICE_ACCOUNT_FILE~: it contains the credentials so that ~traefik~ can update the DNS record for the challenge
+- the environment variable ~GCP_PROJECT~: the name of the GCP project
+- mounting the service account file inside the container (I store it on the host under ~/data/containers/traefik/config/sa.json~)
+
+#+begin_src systemd
+[Unit]
+Description=traefik proxy
+Documentation=https://doc.traefik.io/traefik/
+After=docker.service
+Requires=docker.service
+
+[Service]
+Restart=on-failure
+ExecStartPre=-/usr/bin/docker kill traefik
+ExecStartPre=-/usr/bin/docker rm traefik
+ExecStartPre=/usr/bin/docker pull traefik:latest
+
+ExecStart=/usr/bin/docker run \
+  -p 80:80 \
+  -p 9080:8080 \
+  -p 443:443 \
+  --name=traefik \
+  -e GCE_SERVICE_ACCOUNT_FILE=/var/run/gcp-service-account.json \
+  -e GCE_PROJECT= gcp-super-project \
+  --volume=/data/containers/traefik/config/acme.json:/acme.json \
+  --volume=/data/containers/traefik/config/traefik.yml:/etc/traefik/traefik.yml:ro \
+  --volume=/data/containers/traefik/config/sa.json:/var/run/gcp-service-account.json \
+  --volume=/var/run/docker.sock:/var/run/docker.sock:ro \
+  traefik:latest
+ExecStop=/usr/bin/docker stop traefik
+
+[Install]
+WantedBy=multi-user.target
+#+end_src
+
+As an example, I run [[https://grafana.com/][grafana]] on my home network to view metrics from the various containers / hosts. Let's pretend I use ~example.net~ as my domain. I want to be able to access ~grafana~ via https://dash.example.net. Here's the ~systemd~ unit configuration I use for this:
+
+#+begin_src systemd
+[Unit]
+Description=Grafana in a docker container
+Documentation=https://grafana.com/docs/
+After=docker.service
+Requires=docker.service
+
+[Service]
+Restart=on-failure
+RuntimeDirectory=grafana
+ExecStartPre=-/usr/bin/docker kill grafana-server
+ExecStartPre=-/usr/bin/docker rm grafana-server
+ExecStartPre=-/usr/bin/docker pull grafana/grafana:latest
+
+ExecStart=/usr/bin/docker run \
+  -p 3000:3000 \
+  -e TZ='America/Los_Angeles' \
+  --name grafana-server \
+  -v /data/containers/grafana/etc/grafana:/etc/grafana \
+  -v /data/containers/grafana/var/lib/grafana:/var/lib/grafana \
+  -v /data/containers/grafana/var/log/grafana:/var/log/grafana \
+  --user=grafana \
+  --label traefik.enable=true \
+  --label traefik.http.middlewares.grafana-https-redirect.redirectscheme.scheme=https \
+  --label traefik.http.middlewares.grafana-https-redirect.redirectscheme.permanent=true \
+  --label traefik.http.routers.grafana-http.rule=Host(`dash.example.net`) \
+  --label traefik.http.routers.grafana-http.entrypoints=http \
+  --label traefik.http.routers.grafana-http.service=grafana-svc \
+  --label traefik.http.routers.grafana-http.middlewares=grafana-https-redirect \
+  --label traefik.http.routers.grafana-https.rule=Host(`dash.example.net`) \
+  --label traefik.http.routers.grafana-https.entrypoints=https \
+  --label traefik.http.routers.grafana-https.tls=true \
+  --label traefik.http.routers.grafana-https.tls.certresolver=dash \
+  --label traefik.http.routers.grafana-https.service=grafana-svc \
+  --label traefik.http.services.grafana-svc.loadbalancer.server.port=3000 \
+  grafana/grafana:latest
+
+ExecStop=/usr/bin/docker stop unifi-controller
+
+[Install]
+WantedBy=multi-user.target
+#+end_src
+
+Now I can access my grafana instance via HTTPS (and http://dash.example.net would redirect to HTTPS) while my tailscale interface is up on the machine I'm using (e.g. my desktop or my phone).
diff --git a/users/fcuny/blog/flake.lock b/users/fcuny/blog/flake.lock
new file mode 100644
index 0000000..8af4281
--- /dev/null
+++ b/users/fcuny/blog/flake.lock
@@ -0,0 +1,42 @@
+{
+  "nodes": {
+    "flake-utils": {
+      "locked": {
+        "lastModified": 1649676176,
+        "narHash": "sha256-OWKJratjt2RW151VUlJPRALb7OU2S5s+f0vLj4o1bHM=",
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "rev": "a4b154ebbdc88c8498a5c7b01589addc9e9cb678",
+        "type": "github"
+      },
+      "original": {
+        "owner": "numtide",
+        "repo": "flake-utils",
+        "type": "github"
+      }
+    },
+    "nixpkgs": {
+      "locked": {
+        "lastModified": 1651345462,
+        "narHash": "sha256-4wf9sMUKGBjC2jWnUwG52ychtXRKlS3LfX/HEY6DjhM=",
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "rev": "870249df2cc640e2278cd0bc599ebbb246b00802",
+        "type": "github"
+      },
+      "original": {
+        "owner": "nixos",
+        "repo": "nixpkgs",
+        "type": "github"
+      }
+    },
+    "root": {
+      "inputs": {
+        "flake-utils": "flake-utils",
+        "nixpkgs": "nixpkgs"
+      }
+    }
+  },
+  "root": "root",
+  "version": 7
+}
diff --git a/users/fcuny/blog/flake.nix b/users/fcuny/blog/flake.nix
new file mode 100644
index 0000000..79e6953
--- /dev/null
+++ b/users/fcuny/blog/flake.nix
@@ -0,0 +1,46 @@
+{
+  description = "Franck Cuny's personal website.";
+
+  inputs = {
+    nixpkgs.url = "github:nixos/nixpkgs";
+    flake-utils.url = "github:numtide/flake-utils";
+  };
+
+  outputs = { self, nixpkgs, flake-utils }:
+    flake-utils.lib.eachDefaultSystem (system:
+      let pkgs = nixpkgs.legacyPackages.${system};
+      in {
+        defaultPackage = with pkgs;
+          stdenv.mkDerivation {
+            pname = "fcuny.net";
+            version = self.lastModifiedDate;
+            src = ./.;
+            buildInputs = [ hugo git ];
+            buildPhase = ''
+              mkdir -p $out
+              hugo --minify --destination $out
+            '';
+            dontInstall = true;
+          };
+
+        defaultApp = pkgs.writers.writeBashBin "run-hugo" ''
+          set -e
+          set -o pipefail
+          export PATH=${pkgs.lib.makeBinPath [ pkgs.hugo pkgs.git ]}
+          hugo server -D
+        '';
+
+        apps = {
+          deploy = pkgs.pkgs.writeShellScriptBin "run-deploy" ''
+            set -euxo pipefail
+            export PATH=${
+              pkgs.lib.makeBinPath [ pkgs.hugo pkgs.git pkgs.jq pkgs.flyctl ]
+            }:$PATH
+            bash ./scripts/deploy.sh
+          '';
+        };
+
+        devShell =
+          pkgs.mkShell { buildInputs = with pkgs; [ hugo flyctl git jq ]; };
+      });
+}
diff --git a/users/fcuny/blog/fly.toml b/users/fcuny/blog/fly.toml
new file mode 100644
index 0000000..46468c1
--- /dev/null
+++ b/users/fcuny/blog/fly.toml
@@ -0,0 +1,40 @@
+# fly.toml file generated for fcunynet on 2021-06-20T10:01:50-07:00
+
+app = "fcunynet"
+
+kill_signal = "SIGINT"
+kill_timeout = 5
+
+[build]
+  builtin = "hugo-static"
+
+[env]
+
+[experimental]
+  allowed_public_ports = []
+  auto_rollback = true
+
+[[services]]
+  http_checks = []
+  internal_port = 8080
+  protocol = "tcp"
+  script_checks = []
+
+  [services.concurrency]
+    hard_limit = 25
+    soft_limit = 20
+    type = "connections"
+
+  [[services.ports]]
+    handlers = ["http"]
+    port = 80
+
+  [[services.ports]]
+    handlers = ["tls", "http"]
+    port = 443
+
+  [[services.tcp_checks]]
+    grace_period = "1s"
+    interval = "15s"
+    restart_limit = 6
+    timeout = "2s"
diff --git a/users/fcuny/blog/layouts/_default/baseof.html b/users/fcuny/blog/layouts/_default/baseof.html
new file mode 100644
index 0000000..410e2bc
--- /dev/null
+++ b/users/fcuny/blog/layouts/_default/baseof.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<html lang="en">
+  {{ partial "head.html" . }}
+  <body>
+    {{- partial "header.html" . -}}
+    <main>
+     {{ block "main" . }}{{ end }}
+    </main>
+    {{- partial "footer.html" . -}}
+  </body>
+</html>
diff --git a/users/fcuny/blog/layouts/_default/list.html b/users/fcuny/blog/layouts/_default/list.html
new file mode 100644
index 0000000..d2c59a7
--- /dev/null
+++ b/users/fcuny/blog/layouts/_default/list.html
@@ -0,0 +1,10 @@
+{{ define "main" }}
+
+<article>
+
+  {{ $pgs := where .Data.Pages "Params.hidden" "ne" "true" }}
+  {{ partial "postlist" $pgs }}
+
+</article>
+
+{{ end }}
diff --git a/users/fcuny/blog/layouts/_default/single.html b/users/fcuny/blog/layouts/_default/single.html
new file mode 100644
index 0000000..7a85a05
--- /dev/null
+++ b/users/fcuny/blog/layouts/_default/single.html
@@ -0,0 +1,54 @@
+{{ define "main" }}
+
+<div>
+
+<h1>{{ .Title }}</h1>
+
+<div id="meta">
+  {{- $pub := .Date.Format "Jan 2, 2006" -}}
+  {{- $mod := "" -}}
+  {{- if (not .GitInfo) }}
+  {{- $mod = .Lastmod.Format "Jan 2, 2006" -}}
+  {{ else }}
+  {{- $mod = .Page.GitInfo.CommitDate.Format "Jan 2, 2006" -}}
+  {{ end -}}
+  {{ if eq $pub $mod }}
+  <div id="meta_date">published {{ $pub }}</div>
+  {{ else }}
+  <div id="meta_date">published {{ $pub }} - last modified {{ $mod }}</div>
+  {{ end }}
+  {{ if .Params.tags }}
+  <div>
+  {{ if eq (len .Params.tags) 1 }}
+  tag:
+  {{ else }}
+  tags:
+  {{ end }}
+  {{ range $idx, $tag := .Params.tags }}
+  <span id="meta_tags"><a href="/tags/{{ $tag | urlize }}/">{{ $tag }}</a></span>
+  {{ end }}
+  </div>
+  {{ end }}
+</div>
+
+{{ if .Params.toc }}
+<div id="toc_small">
+  <summary>Table of contents</summary>
+  {{ .TableOfContents }}
+</div>
+{{ end }}
+
+<article>
+{{ .Content }}
+</article>
+
+</div>
+
+{{ if .Params.toc }}
+<div id="toc">
+  <strong>Table of contents</strong>
+  {{ .TableOfContents }}
+</div>
+{{ end }}
+
+{{ end }}
diff --git a/users/fcuny/blog/layouts/index.atom.xml b/users/fcuny/blog/layouts/index.atom.xml
new file mode 100644
index 0000000..1d73f9b
--- /dev/null
+++ b/users/fcuny/blog/layouts/index.atom.xml
@@ -0,0 +1,24 @@
+<feed xmlns="http://www.w3.org/2005/Atom">
+  <title>{{ .Site.Title }}</title>
+  <link href="{{ .Permalink }}feed.xml" rel="self"/>
+  <link href="{{ .Permalink }}"/>
+  <id>{{ .Permalink }}</id>{{ with .Site.Author.name }}
+  <author>
+    <name>{{.}}</name>{{ with $.Site.Author.email }}
+    <email>{{.}}</email>{{end}}
+  </author>{{end}}
+  <generator>Hugo -- gohugo.io</generator>
+  {{ range where (first 10 (where .Site.Pages "Section" "blog")) "Params.hidden" "ne" "true" }}
+  <entry>
+    {{ `<title type="html"><![CDATA[` | safeHTML }}{{ .Title }}]]></title>
+    <link href="{{ .Permalink }}"/>
+    <id>{{ .Permalink }}</id>{{ with .Site.Params.Author }}
+    <author>
+      <name>{{.}}</name>
+    </author>{{end}}
+    {{- $fmt := "2006-01-02T15:04:05-07:00" }}
+    <published>{{ .Date.Format $fmt | safeHTML }}</published>
+    {{ `<content type="html"><![CDATA[` | safeHTML }}{{ .Content }}]]></content>
+  </entry>
+  {{ end }}
+</feed>
diff --git a/users/fcuny/blog/layouts/index.html b/users/fcuny/blog/layouts/index.html
new file mode 100644
index 0000000..38520ef
--- /dev/null
+++ b/users/fcuny/blog/layouts/index.html
@@ -0,0 +1,20 @@
+{{ define "main" }}
+
+<article>
+
+  <p>I'm a Principal SRE, currently working at <a href="https://www.roblox.com/" target="_blank">Roblox</a>. Previously I worked at <a href="https://twitter.com/TwitterEng" target="_blank">Twitter</a> for over 7 years, and my main focus was on Twitter's compute platform.</p>
+
+  <p>My general interests are in building sustainable teams, improving the management and operation of large infrastructure, and work with different teams to implement best practices around reliability and security.</p>
+
+  <h2>Posts</h2>
+  <ul>
+  {{- $pages := where site.RegularPages "Type" "in" site.Params.mainSections }}
+  {{ range $pages }}
+  {{- $fmt := "2006-01-02" }}
+  <li class="post-permalink"><span class="post-date" >{{ .Date.Format $fmt | safeHTML }}</span>, <a href="{{ .Permalink }}">{{ .Title }}</a></li>
+  {{ end }}
+  </ul>
+
+</article>
+
+{{ end }}
diff --git a/users/fcuny/blog/layouts/partials/footer.html b/users/fcuny/blog/layouts/partials/footer.html
new file mode 100644
index 0000000..e2bf7ab
--- /dev/null
+++ b/users/fcuny/blog/layouts/partials/footer.html
@@ -0,0 +1,32 @@
+<footer>
+  <a href="mailto:franck@fcuny.net" title="franck@fcuny.net">
+    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-envelope-fill" viewBox="0 0 20 20">
+      <path d="M.05 3.555A2 2 0 0 1 2 2h12a2 2 0 0 1 1.95 1.555L8 8.414.05 3.555ZM0 4.697v7.104l5.803-3.558L0 4.697ZM6.761 8.83l-6.57 4.027A2 2 0 0 0 2 14h12a2 2 0 0 0 1.808-1.144l-6.57-4.027L8 9.586l-1.239-.757Zm3.436-.586L16 11.801V4.697l-5.803 3.546Z"/>
+    </svg>
+  </a>
+
+  <a target="_blank" href="https://git.fcuny.net/fcuny" title="git.fcuny.net">
+    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-git" viewBox="0 0 20 20">
+      <path d="M15.698 7.287 8.712.302a1.03 1.03 0 0 0-1.457 0l-1.45 1.45 1.84 1.84a1.223 1.223 0 0 1 1.55 1.56l1.773 1.774a1.224 1.224 0 0 1 1.267 2.025 1.226 1.226 0 0 1-2.002-1.334L8.58 5.963v4.353a1.226 1.226 0 1 1-1.008-.036V5.887a1.226 1.226 0 0 1-.666-1.608L5.093 2.465l-4.79 4.79a1.03 1.03 0 0 0 0 1.457l6.986 6.986a1.03 1.03 0 0 0 1.457 0l6.953-6.953a1.031 1.031 0 0 0 0-1.457"/>
+    </svg>
+  </a>
+
+  <a target="_blank" href="https://twitter.com/franckcuny" title="@franckcuny">
+    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-twitter" viewBox="0 0 20 20">
+      <path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"/>
+    </svg>
+  </a>
+
+  <a href="/ssh.pub.sig" title="SSH public keys">
+    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-filetype-key" viewBox="0 0 20 20">
+      <path fill-rule="evenodd" d="M14 4.5V14a2 2 0 0 1-2 2h-1v-1h1a1 1 0 0 0 1-1V4.5h-2A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v9H2V2a2 2 0 0 1 2-2h5.5L14 4.5ZM3.21 11.85h-.87L.83 13.64H.79v-1.79H0v3.999h.791v-1.283l.41-.466 1.12 1.749h.951l-1.488-2.276 1.427-1.723Zm2.903 3.352h-1.79v-1.073h1.685v-.606H4.323v-1.025h1.79v-.648H3.538v3.999h2.575v-.647Zm2.243-.888v1.535h-.794v-1.52L6.223 11.85H7.1l.853 1.696h.032l.855-1.696h.856l-1.339 2.464Z"/>
+    </svg>
+  </a>
+
+  <a href="/feed.xml" title="atom feed">
+    <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-rss-fill" viewBox="0 0 20 20">
+      <path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm1.5 2.5c5.523 0 10 4.477 10 10a1 1 0 1 1-2 0 8 8 0 0 0-8-8 1 1 0 0 1 0-2zm0 4a6 6 0 0 1 6 6 1 1 0 1 1-2 0 4 4 0 0 0-4-4 1 1 0 0 1 0-2zm.5 7a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z"/>
+    </svg>
+  </a>
+
+</footer>
diff --git a/users/fcuny/blog/layouts/partials/head.html b/users/fcuny/blog/layouts/partials/head.html
new file mode 100644
index 0000000..7de4fd4
--- /dev/null
+++ b/users/fcuny/blog/layouts/partials/head.html
@@ -0,0 +1,18 @@
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+
+  <link rel="canonical" href="{{ .Permalink }}">
+
+  {{ $css := "/css/custom.css"  }}
+  <link rel="stylesheet" href="{{ $css }}">
+  <link rel="alternate" href="{{ "/feed.xml" | relURL }}" type="application/atom+xml" title="ATOM feed">
+  <link rel="author" href="humans.txt" />
+
+  <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🖥️</text></svg>">
+
+  <meta name="description" content="Franck Cuny's website, with articles about computers stuff.">
+  <meta name="author" content="Franck Cuny">
+
+  <title>{{ .Title }}</title>
+</head>
diff --git a/users/fcuny/blog/layouts/partials/header.html b/users/fcuny/blog/layouts/partials/header.html
new file mode 100644
index 0000000..65900eb
--- /dev/null
+++ b/users/fcuny/blog/layouts/partials/header.html
@@ -0,0 +1,10 @@
+<header>
+  <nav class="navbar">
+    <a class="nav-bold" href="{{ .Site.BaseURL }}">~/blog</a>
+    <ul class="nav-links">
+      {{ range .Site.Menus.main }}
+      <li class="nav-item"><a class="nav-bold" href="{{ absURL .URL }}">{{ .Title }}</a></li>
+      {{ end }}
+    </ul>
+  </nav>
+</header>
diff --git a/users/fcuny/blog/layouts/partials/postlist.html b/users/fcuny/blog/layouts/partials/postlist.html
new file mode 100644
index 0000000..3d5cf04
--- /dev/null
+++ b/users/fcuny/blog/layouts/partials/postlist.html
@@ -0,0 +1,12 @@
+{{ range .GroupByDate "2006" }}
+  <h2>{{ .Key }}</h2>
+  <ul>
+    {{ range .Pages.ByDate.Reverse }}
+    <li>
+      <div>
+        <a href="{{ .Permalink }}">{{ .Title }}</a>
+      </div>
+    </li>
+    {{ end }}
+  </ul>
+{{ end }}
diff --git a/users/fcuny/blog/layouts/taxonomy/tag.html b/users/fcuny/blog/layouts/taxonomy/tag.html
new file mode 100644
index 0000000..2b7d98a
--- /dev/null
+++ b/users/fcuny/blog/layouts/taxonomy/tag.html
@@ -0,0 +1,6 @@
+{{ define "main" }}
+
+  {{ $pgs := where .Data.Pages "Params.hidden" "ne" "true" }}
+  {{ partial "postlist" $pgs }}
+
+{{ end }}
diff --git a/users/fcuny/blog/scripts/deploy.sh b/users/fcuny/blog/scripts/deploy.sh
new file mode 100755
index 0000000..69ad82a
--- /dev/null
+++ b/users/fcuny/blog/scripts/deploy.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+git diff --exit-code
+git diff --staged --exit-code
+
+flyctl deploy
+
+VERSION=$(flyctl info -j |jq -r '.App | "fcuny.net/v\(.Version)"')
+
+git tag -a --message ${VERSION} ${VERSION}
+git push origin --all
+git push origin --tags
+
+flyctl agent stop
diff --git a/users/fcuny/blog/static/CNAME b/users/fcuny/blog/static/CNAME
new file mode 100644
index 0000000..7398ba2
--- /dev/null
+++ b/users/fcuny/blog/static/CNAME
@@ -0,0 +1 @@
+fcuny.net
\ No newline at end of file
diff --git a/users/fcuny/blog/static/css/custom.css b/users/fcuny/blog/static/css/custom.css
new file mode 100644
index 0000000..70ce618
--- /dev/null
+++ b/users/fcuny/blog/static/css/custom.css
@@ -0,0 +1,201 @@
+body {
+  font-family: sans-serif;
+  font-size: 1em;
+  line-height: 1.8em;
+  color: #0e0e0b;
+  margin: 1em auto;
+  padding: 0 0.55em;
+  max-width: 50rem;
+}
+
+h1 {
+  color: #0e0e0b;
+  font-size: 2rem;
+  margin-top: 1em;
+  margin-bottom: 0.34em;
+}
+
+h2, h3 {
+  border-bottom: 1px solid #eee;
+  font-style: italic;
+}
+h2 {
+  margin-top: 1.25em;
+  margin-bottom: 0.41em;
+  font-size: 1.4rem;
+}
+h3 {
+  margin-top: 1.5em;
+  margin-bottom: 0.5em;
+  font-size: 1.2rem;
+}
+
+hr{
+  color:#000111;
+  background-color:#000111;
+  border:none;
+  height:1px
+}
+
+a {
+  color:#047bc2;
+  transition:color .1s ease-in-out;
+}
+
+a:link,
+a:hover,
+a:focus,
+a:active {
+  color:#047bc2;
+  text-decoration: underline;
+  text-underline-offset:.2rem
+}
+
+span.published, span.updated {
+  display: center;
+  font-style: oblique;
+}
+
+code {
+  font-family: monospace;
+  padding-left: 0.2em;
+  padding-right: 0.2em;
+  border-radius: 4px;
+}
+
+p code {
+  color: black;
+  background-color: #eee;
+  padding: 0 0.2rem;
+  font-size: 1.1em;
+}
+
+pre {
+  font-family: monospace;
+  font-size: 1.1em;
+  margin: 0;
+  word-wrap: normal;
+  padding: 0.8em;
+  overflow-x: auto;
+  border: 1px solid #eee;
+  border-radius: 3px;
+  background-color: #fafafa;
+}
+
+#meta {
+  display: row;
+}
+
+#meta_tags {
+  border-radius: 8px;
+  padding: 0 .5rem;
+  font-size: 0.9rem;
+  border: 2px solid #eee;
+  background-color: #eee;
+}
+
+#meta_tags a {
+  text-decoration: none;
+  border-bottom: none;
+  color: #005a9c;
+}
+
+#meta_date {
+  font-style: italic;
+  font-size: 0.9rem;
+}
+
+table {
+  width: 100%;
+  border-spacing: 0px;
+  outline: none;
+}
+
+td{
+  padding-right: 0.7em;
+  padding-top: 0.4em;
+  padding-bottom: 0.4em;
+}
+thead {
+    color: #000;
+    font-style: bold;
+    text-align: left;
+}
+table, th, td {
+    font-family: monospace;
+    color: #000;
+}
+
+blockquote {
+  font-size: 0.95rem;
+  font-style: italic;
+  margin: 0 0 1.5em;
+  padding-left: 1em;
+  border-left: .2em solid #bdbdbd
+}
+
+nav {
+  padding-right: 10px;
+  font-size: 1.4em;
+  display: flex;
+  font-family: monospace;
+  justify-content: space-between;
+  align-items: center;
+  padding-top: 0.5rem;
+}
+
+.nav-links {
+  list-style: none;
+  display: flex;
+}
+
+.navbar a {
+  display: inline-block;
+  text-decoration: none;
+}
+
+.navbar a:hover {
+  background-color: #b72d2d;
+  color: #fafafa;
+  text-decoration: none;
+}
+
+.nav-bold {
+  font-weight: 700;
+  color: #b72d2d;
+  text-decoration: none;
+}
+
+article {
+  text-align: justify;
+}
+
+.post-permalink {
+  list-style: none;
+  margin-left: -20px;
+}
+
+.post-date {
+  font-family: monospace;
+  font-weight: 400;
+  font-size: 1.1em;
+}
+
+footer {
+  border-top: 2px solid #eee;
+  margin-top: 2em;
+  display: flex;
+  flex-direction: row;
+  justify-content: left;
+  align-items: left;
+}
+
+footer a, footer a:link, footer a:focus, footer a:active, footer a:hover {
+  color: black;
+  text-decoration: none;
+  padding: 5px;
+}
+
+footer a:not(:first-child) {
+  margin-left: 15px;
+}
diff --git a/users/fcuny/blog/static/humans.txt b/users/fcuny/blog/static/humans.txt
new file mode 100644
index 0000000..d2c9d47
--- /dev/null
+++ b/users/fcuny/blog/static/humans.txt
@@ -0,0 +1,10 @@
+/* TEAM */
+Author: Franck Cuny
+Contact: franck [at] fcuny.net
+Twitter: @franckcuny
+Location: Berkeley, CA
+
+/* SITE */
+CA: Let's Encrypt
+Host: Fly.io
+Serving: Linux, Go
diff --git a/users/fcuny/blog/static/profile.jpg b/users/fcuny/blog/static/profile.jpg
new file mode 100644
index 0000000..5417f13
--- /dev/null
+++ b/users/fcuny/blog/static/profile.jpg
Binary files differdiff --git a/users/fcuny/blog/static/resume.html b/users/fcuny/blog/static/resume.html
new file mode 100644
index 0000000..0f1cd83
--- /dev/null
+++ b/users/fcuny/blog/static/resume.html
@@ -0,0 +1,209 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" lang xml:lang>
+<head>
+  <meta charset="utf-8" />
+  <meta name="generator" content="pandoc" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
+  <meta name="author" content="franck@fcuny.net" />
+  <title>Franck Cuny</title>
+  <style>
+    code{white-space: pre-wrap;}
+    span.smallcaps{font-variant: small-caps;}
+    span.underline{text-decoration: underline;}
+    div.column{display: inline-block; vertical-align: top; width: 50%;}
+    div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
+    ul.task-list{list-style: none;}
+    .display.math{display: block; text-align: center; margin: 0.5rem auto;}
+  </style>
+  <style type="text/css">body {
+font-family: sans-serif;
+font-size: 1em;
+line-height: 1.8em;
+color: #0e0e0b;
+margin: 1em auto;
+padding: 0 0.55em;
+max-width: 50rem;
+}
+h1 {
+color: #0e0e0b;
+font-size: 1.3rem;
+}
+h2, h3 {
+border-bottom: 1px solid #eee;
+font-style: italic;
+}
+h2 {
+margin-top: 1.25em;
+margin-bottom: 0.41em;
+font-size: 1.2rem;
+}
+h3 {
+margin-top: 1.5em;
+margin-bottom: 0.5em;
+font-size: 1rem;
+}
+hr{
+color:#000111;
+background-color:#000111;
+border:none;
+height:1px
+}
+a {
+color:#047bc2;
+transition:color .1s ease-in-out;
+}
+table {
+width: 100%;
+border-spacing: 0px;
+outline: none;
+}
+td{
+padding-right: 0.7em;
+}
+td:last-child {
+text-align: right;
+}
+table, th, td {
+font-family: monospace;
+color: #000;
+}
+#title-block-header {
+padding-right: 10px;
+font-size: 1.4em;
+display: flex;
+font-family: monospace;
+justify-content: space-between;
+align-items: center;
+padding-top: 0.5rem;
+border-bottom: 1px;
+}
+#experience {
+padding-top: 20px;
+}
+</style>
+  <!--[if lt IE 9]>
+    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
+  <![endif]-->
+</head>
+<body>
+<header id="title-block-header">
+<h1 class="title">Franck Cuny</h1>
+<p class="author"><a href="mailto:franck@fcuny.net">franck@fcuny.net</a></p>
+</header>
+<p>I&#39;m a seasoned Site Reliability Engineer with experience in large
+scale distributed systems. I&#39;m invested in mentoring junior and senior
+engineers to help them increase their impact. I&#39;m always looking to
+learn from those around me.</p>
+<p><strong>Specializations</strong>: distributed systems,
+containerization, debugging, software development, reliability.</p>
+<h1 id="experience">Experience</h1>
+<h2 id="roblox-san-mateo">Roblox, San Mateo</h2>
+<table>
+<tbody>
+<tr class="odd">
+<td>Site Reliability Engineer</td>
+<td>Principal (IC6)</td>
+<td>SRE Group</td>
+<td>Feb 2022 - to date</td>
+</tr>
+</tbody>
+</table>
+<p>I&#39;m the Team Lead for the Site Reliability group that was started at
+the end of 2021.</p>
+<p>I&#39;m defining the road-map and identify areas where SREs can partner
+with different team to improve overall reliability of our services.</p>
+<h2 id="twitter-san-francisco">Twitter, San Francisco</h2>
+<h3 id="compute">Compute</h3>
+<table>
+<tbody>
+<tr class="odd">
+<td>Software Engineer</td>
+<td>Senior Staff</td>
+<td>Compute Info</td>
+<td>Aug 2021 - Jan 2022</td>
+</tr>
+<tr class="even">
+<td>Site Reliability Engineer</td>
+<td>Senior Staff</td>
+<td>Compute SREs</td>
+<td>Jan 2018 - Aug 2021</td>
+</tr>
+</tbody>
+</table>
+<p>Initially the Tech Lead of a team of 6 SREs supporting the Compute
+infrastructure. In August 2021 I changed to be a Software Engineer and
+was leading one of the effort to adopt Kubernetes for our on-premise
+infrastructure. As a Tech Lead I helped define number of internal
+processes for the team, from on-call rotations to postmortem
+processes.</p>
+<p>Twitter&#39;s Compute is one of the largest Mesos cluster in the world
+(XXX thousands of nodes across multiple data centers). The team defined
+KPIs, improved automation to mange the large fleet of bare metal
+machines, defined APIs for maintenance with partner teams.</p>
+<p>In addition to supporting Aurora/Mesos, I also lead a number of
+effort related to Kubernetes, both on-premise and in the cloud.</p>
+<p>Finally, I&#39;ve helped Twitter save XX of millions of dollar in
+hardware by designing and implementing strategies to significantly
+improve the hardware utilization of our bare metal infrastructure.</p>
+<h3 id="storage">Storage</h3>
+<table>
+<tbody>
+<tr class="odd">
+<td>Site Reliability Engineer</td>
+<td>Staff</td>
+<td>Storage SREs</td>
+<td>Aug 2014 - Jan 2018</td>
+</tr>
+</tbody>
+</table>
+<p>For 4 years I supported the Messaging and Manhattan teams. I moved
+all the pub-sub systems from bare-metal deployment to Aurora/Mesos,
+being the first storage team to adopt the Compute orchestration
+platform. This helped reducing operations, time to deploy, and improve
+overall reliability. I pushed for adopting 10Gb+ networking in our data
+center to help our team to scale. I was the SRE Tech Lead for the
+Manhattan team, helping with performance, operation and automation.</p>
+<h2 id="senior-software-engineer---say-media-san-francisco">Senior
+Software Engineer - Say Media, San Francisco</h2>
+<table>
+<tbody>
+<tr class="odd">
+<td>Software Engineer</td>
+<td>Senior SWE</td>
+<td>Infrastructure</td>
+<td>Aug 2011 - Aug 2014</td>
+</tr>
+</tbody>
+</table>
+<p>During my time at Say Media, I worked on two different teams. I
+started as a software engineer in the platform team building the various
+APIs; I then transitioned to the operation team, to develop tooling to
+increase the effectiveness of the engineering organization.</p>
+<h2 id="senior-software-engineer---linkfluence-paris">Senior Software
+Engineer - Linkfluence, Paris</h2>
+<table>
+<tbody>
+<tr class="odd">
+<td>Software Engineer</td>
+<td>Senior SWE</td>
+<td>Infrastructure</td>
+<td>July 2007 - July 2011</td>
+</tr>
+</tbody>
+</table>
+<p>I was one of the early engineers joining Linkfluence in 2007. I led
+the development of the company&#39;s crawler (web, feeds). I was responsible
+for defining the early architecture of the company, and designed the
+internal platforms (Service Oriented Architecture). I helped the company
+to contribute to open source projects; contributed to open source
+projects on behalf of the company; represented the company at numerous
+open sources conferences in Europe.</p>
+<h1 id="technical-skills">Technical Skills</h1>
+<ul>
+<li><strong>Languages</strong> Python, Go, Ruby, Perl</li>
+<li><strong>Frameworks</strong> Kubernetes, Aurora, Mesos</li>
+<li><strong>Databases</strong> RDBMS, NOSql</li>
+<li><strong>Dev tools</strong> Git</li>
+</ul>
+</body>
+</html>
diff --git a/users/fcuny/blog/static/resume.pdf b/users/fcuny/blog/static/resume.pdf
new file mode 100644
index 0000000..123b9ef
--- /dev/null
+++ b/users/fcuny/blog/static/resume.pdf
Binary files differdiff --git a/users/fcuny/blog/static/ssh.pub.sig b/users/fcuny/blog/static/ssh.pub.sig
new file mode 100644
index 0000000..88f4f3e
--- /dev/null
+++ b/users/fcuny/blog/static/ssh.pub.sig
@@ -0,0 +1,2 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC1rWKrdSHxlAZnRv1F5jUsHgXSNmr1KzllWEn+JqA7p3zxmSEPBbfIUGxSzkFIQrSbKizJLdH6hGA8DcIm+e+ldQ2RYOdiYBxIkPm+aHB6dw7QGNbnSSdkr9gKThy65j0YOOcmuDExjqxfq6O/8AVstmPH36sUXEIks5F/+WiF+5ehzoJVFqClB1di6w1lml86d0ShrUacgM/ieFPe1vKrzW8ZOM+LaUoGWBTLla1y6UkIqnb7OinmgPu6QAzF6GA7tYJMoHkyV7Axzc2j1/VxVIrUrfY4b0k8lGAzi2GfByq+fXEHzePbaqi8Cy8Trn9eN/ls1WBMUQfSChQi3tM2Vx2BuiOpx/QkXsdgqwe7bTCijcQS7GoREL1qd8tR9sWWd4WMPUiC9kmzvyja5F39xHPgm0A5MtYY7GvQaUPbtBc6g8YuFLLnkqFVEKHSLFiGYP5jIDNvMd5rSSsBUrepCIzWdpprwnKxAjebw5Cyl5p/0MY2zppQRW7AZXehQa7Bv+OClbutEjBa+ioeUxBhezu2rB61XSenTbbUVB5DncD8ceD5AbL9aFz/Bcw6q0kAOGmR1G1MOLgxVHlqcnI5x0E1K2WMKWgQb+1BMek1p5+l3xWNDF4URhLqLupnP5CMrK9ifBOe/76zqyMVrA/mc6tNC58KHhME1IynC1zaLw== franck@fcuny.net
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDg2P92WEDsI2mn2QEk1uSg9MKbj7JgE3pEfI6bWo5fbcnUbVGGZxafK0e7rnSfgWHHyhVeH8W4fQfPb+Iyt/9GLkKEq3aqShVEq+h2FXu7iqdrsANp27UnanuHuL2MzQY041jAiqrmJUBzCedJfnmlydMoo0TdK2Hxzu3cLjaI0VhEySo8c/NKuUB6uIdfuA8dkvIdP2O0owpLoKOGDdsDTedhGiN+mQgB+Z2+oYM4XOVmcMY6/f2cxXRBCkktcy9s451NqhjmsVB/L1n1GjJlOk+mQ6wGjR7KN7bgJtBC2sE/WuiZL+pmOlu3dF9vLyWw7iPHj7YvLlK6gztBHZT2v694Thqd9yb/ESOFfyrcujZu0qkRhjPYOz7VJC9F9PmTIVW6Zk4kknQzh9pmKWJiDNZEaQ88cXGn+HBj9aZ/fj3h6TyowtLZkXWebNcUHFMquuTMpJigFq+bSvus5Ad+v5L185CNa/9o3P1TNAft4YF3IR0WfJ08aNcFBYkiOXivpuTmbMzuRKUQ/5dhAlOuqTSzDDEFa6Q9MMo2oorNcZhjrfKq/T7gKXKqG8DuWNGKsM1ZduduCjunuWJw9SjUHVEwW2NtNHcvRWP19HFy/+T5rWDBEUUSYQba34xnvMRa9NZLeqGC+korXCUjxjQEM8aFpCUmqdwY6QBgpyGAbw== franck@fcuny.net