https://github.com/symkat/MyJekyllBlog Skip to content Toggle navigation Sign up * Product + Actions Automate any workflow + Packages Host and manage packages + Security Find and fix vulnerabilities + Codespaces Instant dev environments + Copilot Write better code with AI + Code review Manage code changes + Issues Plan and track work + Discussions Collaborate outside of code + Explore + All features + Documentation + GitHub Skills + Blog * Solutions + For + Enterprise + Teams + Startups + Education + By Solution + CI/CD & Automation + DevOps + DevSecOps + Case Studies + Customer Stories + Resources * Open Source + GitHub Sponsors Fund open source developers + The ReadME Project GitHub community articles + Repositories + Topics + Trending + Collections * Pricing [ ] * # In this repository All GitHub | Jump to | * No suggested jump to results * # In this repository All GitHub | Jump to | * # In this user All GitHub | Jump to | * # In this repository All GitHub | Jump to | Sign in Sign up {{ message }} symkat / MyJekyllBlog Public * Notifications * Fork 0 * Star 24 This is a multi-user CMS and hosting platform for Jekyll blogs. License MIT license 24 stars 0 forks Star Notifications * Code * Issues 0 * Pull requests 0 * Actions * Projects 0 * Security * Insights More * Code * Issues * Pull requests * Actions * Projects * Security * Insights symkat/MyJekyllBlog This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. master Switch branches/tags [ ] Branches Tags Could not load branches Nothing to show {{ refName }} default View all branches Could not load tags Nothing to show {{ refName }} default View all tags Name already in use A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch? Cancel Create 1 branch 0 tags Code * Local * Codespaces * Clone HTTPS GitHub CLI [https://github.com/s] Use Git or checkout with SVN using the web URL. [gh repo clone symkat] Work fast with our official CLI. Learn more. * Open with GitHub Desktop * Download ZIP Sign In Required Please sign in to use Codespaces. Launching GitHub Desktop If nothing happens, download GitHub Desktop and try again. Launching GitHub Desktop If nothing happens, download GitHub Desktop and try again. Launching Xcode If nothing happens, download Xcode and try again. Launching Visual Studio Code Your codespace will open once ready. There was a problem preparing your codespace, please try again. Latest commit Manager Bot Update static pages. ... 890cbf7 Dec 16, 2022 Update static pages. 890cbf7 Git stats * 6 commits Files Permalink Failed to load latest commit information. Type Name Latest commit message Commit time .github Add images for README. Dec 16, 2022 DB initial commit Dec 10, 2022 Web Update static pages. Dec 16, 2022 ansible initial commit Dec 10, 2022 LICENSE initial commit Dec 10, 2022 README.md Update README. Dec 16, 2022 View code [ ] MyJekyllBlog Screenshots Table of Contents Meet The Servers Panel Build Store Certbot WebServer Overview Installation Guide Designing The Network Running The Installation With Ansible Configure The Panel Configure The Store Step 3: Confirm It All Works! Operations Guide How to perform a system backup Backup Gitea Data Backup PSQL Data How to perform a system restore Restore Gitea Data Restore PSQL Data Development Guide Enable development mode Reload Default Minion Worker Reload Certbot Minion Worker Extend the DB Schema Run Jekyll Roadmap Themes Email Subscription / Notification Better Front-Matter UI README.md MyJekyllBlog This is MyJekyllBlog: it's a multi-user CMS and hosting platform for Jekyll blogs. Multi-user means many different people can create accounts and make their own blogs. User registration can be open, invite-only, or require payment with stripe. The CMS feature means that those same people can create posts, pages, upload media for their blogs or edit and delete those same kinds of things. With the included hosting platform, these blogs can be be displayed directly to people on the Internet. MyJekyllBlog comes with a complete set of ansible roles to automate the installation. This software runs my paid web hosting offering: MyJekyllBlog.com, please consider registering an account. Screenshots User Post The post listing page, where you can add, edit, or delete blog posts. User Post Creator The post creator, where you can add a new blog post. User Jobs The jobs interface, where you can see the progress of your site's deployment and other tasks. Admin Servers Panel The admin interface's server panel. Where servers the blogs are deployed to can add added and removed. Table of Contents 1. MyJekyllBlog 1. Table Of Contents 2. Meet The Servers 1. Panel 2. Build 3. Store 4. Certbot 5. WebServer 3. Overview 2. Installation Guide 1. Designing The Network 2. Running The Installation With Ansible 3. Configure The Panel 4. Configure The Store 3. Operations Guide 1. How to perform a system backup 1. Backup Gitea Data 2. Backup PSQL Data 2. How to perform a system restore 1. Restore Gitea Data 2. Restore PSQL Data 4. Development Guide 1. Enable development mode 2. Reload Default Minion Worker 3. Reload Certbot Minion Worker 4. Extend the DB Schema 5. Run Jekyll 5. Roadmap Meet The Servers This table shows a brief overview of the server types and their relationships. Additional descriptions for each server type follows the table. Server Description Services Talks To Panel Runs customer-facing web mjb.panel, build, store interface nginx Build Runs site builders, deploys mjb.worker store, blogs webservers Store Source of truth - Database, postgresql, panel, build, Gitea gitea certbot Certbot Handles getting/updating SSL mjb.certbot store, certs webservers WebServer Hosts customer blogs on the nginx certbot, build internet Panel The Panel server hosts the web application that customers can use to provision blogs, publish articles, upload media and otherwise manage their blogs. Administrators can use it to check users/blogs on the system, run maintenance tasks, and configure some aspects of the system. Build The build server processes Jekyll git repositories into static websites and deploys the fully built website to the webservers for hosting. Store The store server hosts two database with postgresql. One database supports MyJekyllBlog::DB and another supports Minion. The Panel, Build, and Certbot servers all need access to these databases. The store server also hosts an installation of Gitea so that each Jekyll blog may have its own central git repository. The panel server will checkout and commit/push to this server. The build server will checkout the repository from this server for building. Certbot The CertBot server handles obtaining SSL certificates from Let's Encrypt and pushing them to the webservers. When HTTP challenges are used, /.well-known/ is proxied from ALL webserver nodes to the certbot node and --standalone is used from the certbot node to obtain an SSL certificate. When DNS challenges are used, wildcard certificates may be obtained (and is recommended for hosts expecting many sub-domains to be made). The /etc/letsencrypt directory is synced with webserver nodes through rsync whenever new certificates are obtained. An administrator can update and sync SSL certificates from the admin panel. WebServer WebServers run nginx and host static content for Jekyll blogs. When a blog is provisioned, an SSL certificate will be requested for the site and an nginx configuration file will be created. The build servers will sync the blog content with webservers each time the blog is updated through the Panel. Overview This diagram shows a view of a user updating a post and somebody from the Internet viewing a post. flowchart TB subgraph one[Panel] a2[MyJekyllBlog::Web] a3[Nginx] a3 -- Hypnotoad PSGI --> a2 end subgraph two[Build] b2[MyJekyllBlog Worker

- Pulls blog repo
- Builds blog with jekyll
- Deploys to each WebServer] end subgraph four[Store] d1[PostgreSQL] d2[Gitea] d2 <-- Second: Minion fetches repo --> b2 d1 <-- First: Minion Picks Up Job --> b2 a2 -- Queue blog sync job --> d1 a2 -- Update file in repo --> d2 end subgraph three[WebServer] c1[nginx] end b2 -- rsync to each WebServer Node --> c1 q[Internet User] <-- View Blog Post -->c1 z[MyJekyllBlog User] <-- Submit Post Update -->a3 Installation Guide This guide will follow my process of installing the software to run on mds-stage.com and serve blogs on mds-stage-blog.com. If you follow along, you could have your own version up and running in a couple of hours. Designing The Network I need at least one of each server type. I will, however, use two WebServers and have one on the west coast and another on the east coast. I will need six servers, and I will name them panel, store, build, certbot, web-west, west-east. I want the database server running on store not to be exposed to the Internet at large, and I will use the private networking feature of my VPS provider to get private IPs for panel, store, build, and certbot. This means I also need to have all of those machines running in the same datacenter. Each machine should be running Debian 11. Machine Public IP Private IP Domain panel 45.79.31.186 192.168.213.90 panel.mjb-stage.com build 45.33.25.211 192.168.188.226 build.mjb-stage.com store 69.164.204.212 192.168.216.75 store.mjb-stage.com certbot 96.126.122.198 192.168.163.105 certbot.mjb-stage.com web-west 173.255.249.43 N/A web-west.mjb-stage.com web-east 173.255.225.48 N/A web-east.mjb-stage.com Once I have these machines provisioned I lay out the information about them in the table above. I will need this information to begin writing the configuration file. For each of these machines, I update DNS records so that the domain maps to the public IP address for each machine. Additionally, I add two DNS records for *.mds-stage-blog.com, one A record with 173.255.249.43 and another A record with 173.255.225.48. This maps all sub-domains of mds-stage-blog.com to the web servers so that they may serve the blogs to people on the Internet. People on the Internet will go to one or the other server. Before proceeding from this section, review the section checklist to ensure you have completed all item. X Section Checklist Items [ ] Provision machine for panel, build, store, certbot [ ] Provision one or more machines for webservers [ ] Create table with your machine information [ ] Add DNS records for each machine [ ] Add wildcard DNS record for each WebServer Running The Installation With Ansible The installation process has been codified into a number of ansible roles that are included in this repository. I will need ansible installed so that I have access to the ansible-playbook command, and I will need to have SSH credentials to each of these machines so that ansible may install and configure the network. From the root directory of a clone of this repository, I do the following. cd ansible/ mkdir -p env/stage # Copy and edit the inventory file for your network. cp env/example/inventory.yml env/stage/inventory.yml vim env/stage/inventory.yml # Copy and edit the secrets for your network cp env/example/vault.yml env/stage/vault.yml vim env/stage/vault.yml # Create a vault password to encrypt your secrets with perl -e'print join("", map { ('A'..'Z','a'..'z',0..9)[int rand 62] } ( 0 .. 128 )), "\n"' > .vault_password # Encrypt your secrets with the vault password ansible-vault encrypt --vault-password-file .vault_password env/stage/vault.yml I named the configuration file env/stage/inventory.yml, since this will be a staging environment. I placed this in its own directory because some environment specific files will be stored in the inventory directory, and keeping seperate directories will prevent file clobbering. One should pay special attention to go through this example config file and update it with details of their network. I updated the vault file with new passwords and then encrypted it. Once this is complete, the installation should be smooth sailing with ansible. I use the following command to get everything installed. ansible-playbook -i env/stage/inventory.yml --vault-password-file .vault_password -e @env/stage/vault.yml site.yml This command took about two and a half hours to complete, it should largely setup the whole platform across all of the machines. Before proceeding from this section, review the section checklist to ensure you have completed all item. X Section Checklist Items [ ] Checked repo out to a machine with ansible and ssh access [ ] Network specific ansible inventory file was created [ ] Ansible runs through the entire playbook with no errors Configure The Panel An initial admin account is created during the ansible installation. The credentials for the admin account are in the inventory.yml file under admin_user:. Now that I have the admin account credentials, I can access the Servers tab at https://panel.mjb-stage.com/admin/servers The tab configures web servers that the system will deploy blogs to. Each of the webservers that were configures by Ansible should go here, so I enter web-west.mjb-stage.com and then web-west.mjb-stage.com. The servers tab now lists these two servers. Next I need to go to the Domains tab and add mjb-stage-blog.com. There is a drop down for SSL Challenge. When selecting HTTP, each time a blog is added, certbot will be used to complete an HTTP challenge for the domain. When selecting DNS-Linode, a Wildcard SSL certificate will be obtained and then blogs will not need their own certificates. DNS-Linode requires a Linode account and API credentials. X Section Checklist Items [ ] Created admin account, can login and view Admin Panel [ ] Added Web Servers to Admin Panel -> Servers [ ] Added Hosted Domains to Admin Panel -> Domains Configure The Store During the installation process, an SSH keypair was created. The public key must be added to the Gitea user that was setup. This must be done through the Gitea web panel. 1. Login to Gitea on the store server, using the credentials for gitea user/pass from the inventory file. 2. Click the user drop down in the upper right 3. Click Settings from the drop down menu 4. Click "SSH / GPG Keys" 5. Click "Add Key" under "Manage SSH Keys" 6. Type a title 7. Paste the contents of env/staging/files/ssh/id_rsa.pub 8. Click to add the key Once this is done, you'll need to create the mjb organization. 1. Click the + Plus button drop down 2. Click "New Organization" 3. Name the organization "mjb" 4. Click "Create Organization" Now we need to add a Jekyll blog as a template. Get a shell into the build server and create a new Jekyll blog. # Create the default blog alias jekyll="podman run -ti --rm -v .:/srv/jekyll -e JEKYLL_ROOTLESS=1 docker.io/jekyll/jekyll jekyll" jekyll new jekyll-default # Push this default to the panel_config.jekyll_init_repo repository cd jekyll-default git init git remote add origin git@store.mjb-stage.com:manager/jekyll-default.git git add * .gitignore git commit -m "Initial Commit" git push origin master Now that the panel_config.jekyll_init_repo repository exists, we should be ready to rock and roll; give provisioning a blog a go. X Section Checklist Items [ ] Confirmed login to the Gitea install on store server [ ] Added SSH key to the gitea user account [ ] Added mjb organization for blog repos to be added under [ ] Pushed the jekyll repo to the jekyll_init_repo address Step 3: Confirm It All Works! 1. Create a user account 2. Create a blog 3. Create a post 4. Delete a post Operations Guide How to perform a system backup There are two sources of truth for MyJekyllBlog that should be backed up regularly. The first is the installation of Gitea, a Git Service, that MyJekyllBlog uses for storage and atomic changes of all Jekyll blogs it hosts. The second is the PSQL database for mjb.panel. This database contains all of the information about user accounts, blog ownership, etc. Backup Gitea Data Take a root shell on the store machine and stop gitea. systemctl stop gitea Confirm that gitea is stopped by going to the store server over HTTPS. You should recieve a 502 Bad Gateway error. su - git cd /var/lib/gitea/ ./gitea dump --c /etc/gitea/app.ini There will be a file with a timestamp, such as gitea-dump-1669000424.zip. This is the backup of all Gitea data and can be used to restore the repositories. Remember to start Gitea again. systemctl start gitea Backup PSQL Data Take a shell for the manager user on the store server and run the following. cd mjb/Web ./script/mjb db_dump > psql-backup-`date +%s`.sql How to perform a system restore Restore Gitea Data See https://docs.gitea.io/en-us/backup-and-restore/# restore-command-restore for information on processing the gitea-dump-timestamp.zip file. Restore PSQL Data Instructions -- I should make an ansible task that handles this. Drop database, create database, import SQL dump. Development Guide Enable development mode To make development easier, you can enable development tools on any given environment with ansible. If I wanted to enable them on the stage environment, I would run the following command. ansible-playbook -i env/stage/inventory.yml --vault-password-file .vault_password -e @env/stage/vault.yml tools-development.yml Some changes to the environment after running the tools-development playbook are: Panel Server * Changes to files in Web/lib and Web/template will cause Mojolicious to reload the app * Docker is installed to use create-classes in DB/ Reload Default Minion Worker The default minion worker handles building Jekyll blogs, syncing files and media, and other tasks that use the default queue. To pull the latest changes and reload it, perform the following, replacing build-server with the hostname of the build server. ssh manager@build-server 'cd mjb; git pull --ff-only origin master' ssh root@build-server 'systemctl restart mjb.worker' Reload Certbot Minion Worker The certbot minion worker handles let's encrypt certificate requests, syncing SSL certificates with web server nodes, and other tasks that use the certbot queue. To pull the latest changes and reload it, perform the following, replacing certbot-server with the hostname of the certbot server. ssh manager@certbot-server 'cd mjb; git pull --ff-only origin master' ssh root@certbot-server 'systemctl restart mjb.certbot' Extend the DB Schema To extend the DB schema you should be on a panel server after Enabling development mode. The database can be extended by editing DB/etc/schema.sql The DBIx::Class models can be regenerated with the updated DB/etc/ schema.sql by running the following. cd mjb/DB ./bin/create-classes Inspect the newly generated DB/lib files. To update an existing database, you may need to form alter table statements to match the changes you made to the schema.sql file. Run Jekyll If you need to use Jekyll directly, shell into a build server and set the following alias. alias jekyll="podman run -ti --rm -v .:/srv/jekyll -e JEKYLL_ROOTLESS=1 docker.io/jekyll/jekyll jekyll" Once you have done so, you should be able to use jekyll directly. Roadmap Themes Currently the default minima theme is installed and there is no obvious way to change the theme. There should be a theme tab that allows selecting themes. Email Subscription / Notification There is no way for users to subscribe and get email notifications when a new post is made. Create an opt-in email system, and a form that MyJekyllBlog users can add to their blog to allow people on the Internet to subscribe for updates. Better Front-Matter UI The UI only supports handling the title and date as UI items, otherwise a user is required to write YAML content. Investigate Jekyll front matter options more and integrate them into the UI (and add a simple editor for page creation that doesn't require YAML) About This is a multi-user CMS and hosting platform for Jekyll blogs. Resources Readme License MIT license Stars 24 stars Watchers 2 watching Forks 0 forks Releases No releases published Packages 0 No packages published Languages * Perl 78.5% * Raku 16.8% * HTML 1.7% * Jinja 1.4% * Python 0.6% * Shell 0.5% * Other 0.5% Footer (c) 2022 GitHub, Inc. Footer navigation * Terms * Privacy * Security * Status * Docs * Contact GitHub * Pricing * API * Training * Blog * About You can't perform that action at this time. You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.