Ghost on NixOS

Ghost on NixOS

В этой заметке описана установка движка блога Ghost на NixOS. Существует более каноничный способ установки без Docker в виде пакета NixOS, но я сделал так, как понятней мне.

MySQL

Ghost задеприкейтил SQLite для боевого режима, поэтому используется MySQL (если точнее, то MariaDB).

{
  services.mysql = {
    enable = true;
    package = pkgs.mariadb;
    settings = {
      mysqld = {
        bind-address = "172.17.0.1";
      };
    };
  };

  networking.firewall.interfaces."docker0".allowedTCPPorts = [ 3306 ];
}

Не очевидные моменты это биндиг сервера БД на интерфейс Docker и необходимость разрешить порт в фаерволе. Это нужно для доступа изнутри контейнера к сервису, запущенном на хосте. У модуля MySQL из Nixpkgs есть возможность добавить пользователя и дать ему нужные права, но этот пользователь создается без пароля и с доступом только с localhost, поэтому пользователя создаем ручками.

$ sudo -u root mysql -u root
CREATE DATABASE 'ghost';
CREATE USER 'ghost'@'%'
  IDENTIFIED VIA unix_socket
  OR mysql_native_password USING PASSWORD("password");
GRANT ALL PRIVILEGES ON ghost.* TO 'ghost'@'%';

Docker container

Сперва нужно включить сервис Docker:

{
  virtualisation.docker.enable = true;
}

Запуск контейнера:

docker run --restart=always -p 127.0.0.1:2368:2368 \
	-e url=https://kysa.me \
	-v /var/www/ghost/content:/var/lib/ghost/content  \
	--add-host=host.docker.internal:host-gateway \
	-e database__client=mysql \
	-e database__connection__host=host.docker.internal \
	-e database__connection__user=ghost \
	-e database__connection__password="password" \
	-e database__connection__database=ghost \
	--name ghost-alpine -d ghost:alpine
  • --restart=always - автозапуск контейнера (при перезагрузке ОС).
  • -p 127.0.0.1:2368:2368 - публикуем порт на котором запущен наш Ghost.
  • -v /var/www/ghost/content:/var/lib/ghost/content - монтирование постоянного хранилища в контейнер (картинки, темы, логи и т.д.).
  • -e url=https:\\kysa.me - URL по которому будет доступен блог. Без этой переменной окружения не будут доступны вложения (картинки).
  • --add-host=host.docker.internal:host-gateway волшебная строчка дающая доступ к сервисам хоста из контейнера. host.docker.internal добавляет в /etc/hosts контейнера запись, указывающую на IP хоста; host-gateway добавляет в контейнер сетевой маршрут на хост.
  • -e database__* - реквизиты доступа к БД.

NGINX frontend

Контейнер с движком блога запущен, теперь нужно организовать к нему доступ из интернета.

{
  # Let`s Encrypt
  security.acme.acceptTerms = true;
  security.acme.defaults.email = "user@example.com";
  security.acme.certs."kysa.me".extraDomainNames = [ "static.kysa.me" ];
  # NGINX frontend
  services.nginx = {
    enable = true;

    recommendedGzipSettings = true;
    recommendedOptimisation = true;
    recommendedProxySettings = true;
    recommendedTlsSettings = true;

    virtualHosts = {
      "kysa.me" = {
        forceSSL = true;
        enableACME = true;
        locations."/ghost/" = {
          extraConfig =
           "allow 10.100.0.0/24;" +
           "deny all;"
           ;
          proxyPass = "http://127.0.0.1:2368";
        };
        locations."/" = {
          proxyPass = "http://127.0.0.1:2368";
        };
      };

      "static.kysa.me" = {
        forceSSL = true;
        useACMEHost = "kysa.me";
        locations."/" = {
          root = "/var/www/static";
        };
      };
    };
  };
}

security.acme.* - получение сертификатов от Let`s Encrypt для HTTPS. Ограничен по IP доступ к админке блога (/ghost, 10.100.0.0/24 - сеть моего VPN). Также добавлен виртуальный хост static.kysa.me для раздачи всякой статики (например Prism.js). Не забываем открыть порты в фаерволе:

{
  networking.firewall.enable = true;
  networking.firewall.allowedTCPPorts = [
    22     # ssh
    80     # http
    443    # https
  ];
}

Prism.js

И последний штрих - добавление Prism.js для подсветки синтаксиса в блоках кода. Скачиваем библиотеку, кладем в директорию из которой NGINX раздает статику. Конечно можно подгружать Prism.js из CDN, но я предпочитаю не добавлять лишних зависимостей. Идем в админку блога, Settings -> Code injection, подключаем библиотеку:

В Site Header вставляем (дополнительно я подстроил размер шрифта для блоков кода):

<link rel="stylesheet" type="text/css" href="https://static.kysa.me/prism/prism.css"/>
<style>
  pre[class*="language-"] {
      font-size: 1em;
  }
</style>

В Site Footer:

<script type="text/javascript" src="https://static.kysa.me/prism/prism.js">
</script>

Subscribe to Заметочки

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe