[{"content":"Hugo is an open-source static site generator written in Go. It typically takes only a few seconds to generate a website and is known as the \u0026ldquo;world\u0026rsquo;s fastest framework for building websites.\u0026rdquo; Users can write blog content using Markdown syntax. Combined with GitHub Pages, you can deploy your own blog without needing a server. It also supports private deployment methods using HTTP servers like Nginx and Apache. This article, based on the deployment process of this blog, details the above two deployment methods, as well as CI/CD automated deployment and internationalization (i18n) configuration.\nEnglish site - Github Pages access URL:\nzoyao.github.io\nChinese site - Nginx private deployment access URL:\nzoyao.site\nHugo Installation Local Hugo installation: It is recommended to install the Hugo client locally when using it for the first time to facilitate debugging. During subsequent use, you can operate without the local Hugo client; simply editing Markdown files locally and uploading them to GitHub will trigger automated deployment. For details, see the GitHub Pages Automated Deployment and Private Automated Deployment sections.\nHugo Installation\nInstallation process - taking Windows winget as an example, installing the Extended version:\n1 2 3 4 5 // Install winget install Hugo.Hugo.Extended // Uninstall winget uninstall --name \u0026#34;Hugo (Extended)\u0026#34; 1 2 3 4 5 6 7 8 9 10 For other operating systems and installation methods, please refer to the [official documentation][hugo-installation]. - Create a New Site Execute the following command to create a new site named `mysite` in the current folder: ```bash hugo new site mysite Preview\nIf executed successfully, you can preview the website locally on port 1313:\n1 hugo server 1 2 3 4 5 6 7 8 - Build Once the build is complete, the static website will be generated in the `public` directory: ```bash hugo Environment Configuration\nHugo manages environment configuration files under the config folder (this directory does not exist by default and needs to be created by the user). The directory structure is as follows:\n1 2 3 4 5 6 7 mysite ├── config.toml └── config ├── development │ └── config.toml └── production └── config.toml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 As shown above, `development` and `production` environment configurations are created. You can specify the configuration file using `--environment development`. If not specified, then: The `hugo server` preview command uses the `development` environment by default. The `hugo` build command uses the `production` environment by default. Startup commands specifying the configuration file: ```bash // Preview hugo server --environment development // Build hugo --environment development Theme Configuration Thanks to Hugo\u0026rsquo;s rich theme library, we can easily and quickly obtain many personalized Hugo themes. You can browse the official theme library to pick a theme you like.\nBelow, we will use the hugo-theme-stack theme as an example.\nGit Initialization\n1 2 3 4 5 // Enter the directory cd mysite // Git initialization git init 1 2 3 4 5 6 7 - Add a submodule for easy theme updates later ```bash // In the mysite/themes directory, pull hugo-theme-stack and create a theme named stack git submodule add [https://github.com/CaiJimmy/hugo-theme-stack.git](https://github.com/CaiJimmy/hugo-theme-stack.git) themes/stack Configure the Theme\nOpen the newly downloaded theme files, copy the configuration file from the themes/stack/exampleSite directory to the root directory, overwrite the original configuration file, edit the configuration file, and set the theme field to the theme name created in the previous step, which is stack.\nPartial configuration description for hugo-theme-stack:\n1 2 3 4 5 6 7 8 9 10 11 // Base URL, used during page jumps baseurl: [https://example.com/](https://example.com/) // Theme name, should be stack here theme: hugo-theme-stack // Website title title: Example Site // Website copyright info copyright: Example Person 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 After configuring, restart to see the theme changes. For other hugo-theme-stack configurations, please refer to the [official documentation][hugo-theme-stack-config]. ## GitHub Pages Automated Deployment CI/CD GitHub Pages is a web hosting service provided by GitHub, which can be used to host static web pages, including blogs, project documentation, etc. Hugo can quickly build static websites and naturally supports deployment using GitHub Pages. - GitHub Pages Configuration To use GitHub Pages, you need to create a repository that starts with your GitHub username and ends with `.github.io`, and it must be a public repository. The specific process is: 1. New repository 2. Repository name: xxxx(username).github.io 3. Public 4. Create repository Once created, open the repository\u0026#39;s Settings, and modify the Default branch to change the default displayed branch. ![Current configuration is main branch](image-4.png) Upload a static `index.html` file to the specified branch of the repository: ```html \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Hello World\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;Hello World\u0026lt;/h1\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Open `https://username.github.io/` to see the static page you just uploaded. Linking the Hugo Repository for Automated Deployment\nHere, we use two different GitHub repositories:\nblog repository \u0026ndash; used to store the original blog files, mainly in Markdown format.\nGitHub Pages repository \u0026ndash; the repository created in the previous step, used to store the built static website.\nThe specific process is as follows:\nConnect the permissions of the two repositories\nOpen the GitHub global settings (not repository settings): Settings / Developer settings / Personal access tokens / Fine-grained tokens / Generate new token.\nToken name can be filled in as you like.\nExpiration: It is recommended to choose No expiration to avoid it becoming unusable after expiring.\nRepository access: For security reasons, it is recommended to choose Only select repositories and select only the GitHub Pages repository you just created.\nPermissions configuration: Change Contents to Read and write.\nSave and copy the automatically generated token.\nAdd the token configuration to the blog repository. The path is: blog repository / Settings / Security / Secrets and variables / Actions / Repository secrets / New repository secret. Name it PERSONAL_TOKEN, and paste the token copied in the previous step into the Secret and save it.\nHugo Automatic Build and Publish\nIn the blog repository\u0026rsquo;s .github/workflows directory, create deploy.yml with the following configuration:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 name: deploy on: # Execute action on git push push: workflow_dispatch: schedule: # Runs everyday at 8:00 AM - cron: \u0026#34;0 0 * * *\u0026#34; jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 with: submodules: true fetch-depth: 0 - name: Setup Hugo uses: peaceiris/actions-hugo@v2 with: hugo-version: \u0026#34;latest\u0026#34; # Hugo build - name: Build Web run: hugo # Push to specified repository after building - name: Deploy Web uses: peaceiris/actions-gh-pages@v3 with: PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }} EXTERNAL_REPOSITORY: zoyao/zoyao.github.io PUBLISH_BRANCH: master PUBLISH_DIR: ./public commit_message: ${{ github.event.head_commit.message }} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 After configuration, when a `git push` is executed, the automated deployment will be triggered. You can see the relevant workflow in the Actions tab of the `blog` repository. ![alt text](image-5.png) ## Private Automated Deployment CI/CD Using GitHub Pages is very convenient for automated deployment, but GitHub domains can be slow to access in certain network environments. If you have this requirement, you can use private deployment to deploy it to your own server. - Private Deployment Here we use Nginx for private deployment. 1. Copy the built static blog files to the private server, under the `/app/blog/` directory. 2. Configure Nginx. In Nginx\u0026#39;s `./nginx/conf.d` directory, create a new configuration file, named here as `blog-8888.conf`: ```nginx server { # Listen on port 8888 listen 8888; # IP access is used here first, it will be changed to domain name later during HTTPS configuration server_name Server_Public_IP; # server_name www.zoyao.site; # Specify the location of static resources location / { root /app/blog; index index.html index.htm; } } 3. After updating the Nginx configuration, access `http://Server_Public_IP:8888/` to see the blog homepage. Automated Deployment\nTo use automated deployment, you first need to configure SSH key login. It is recommended to create a new user first for proper permission isolation.\nUse the adduser command to create a new user, named git here (customizable):\n1 sudo adduser git 1 2 3 4 5 6 2. Directory authorization. You need to authorize `/app/blog` to the user created in the previous step. Switch to the `git` user to execute: ```bash sudo chmod -R 777 /app/blog 3. Switch to the `git` user, create an SSH key, and save it in GitHub secrets. - Switch to the git user: ```bash su git ``` - Create a new directory `.ssh` under the `/home/git` directory and authorize it: ```bash mkdir -p ~/.ssh \u0026amp;\u0026amp; chmod 700 ~/.ssh 1 2 3 4 5 6 7 8 9 10 11 12 - Execute `ssh-keygen` to generate a key pair. It is recommended to use `ed25519` as it is more secure than `rsa`: ```bash ssh-keygen -t ed25519 -C \u0026#34;github-actions-deploy-key\u0026#34; -f ~/.ssh/github_actions ``` - Copy the public key to `authorized_keys`: ```bash sh -c \u0026#39;cat ~/.ssh/github_actions.pub \u0026gt;\u0026gt; ~/.ssh/authorized_keys\u0026#39; - Authorize `authorized_keys`: ```bash chmod 600 ~/.ssh/authorized_keys 1 2 3 4 5 6 - View and copy the contents of the private key for later use: ```bash cat ~/.ssh/github_actions - Use SSH locally to verify if the login is successful. - Add the key configuration to the `blog` repository. The path is: `blog` repository / Settings / Security / Secrets and variables / Actions / Repository secrets / New repository secret. Name it `SERVER_SSH_KEY`, and paste the private key copied in the previous step into the Secret and save it. ![alt text](image-6.png) 4. Configure GitHub Automated Deployment In the `blog` repository's `.github/workflows` directory, create `deploy.yml`. The configuration content is as follows. Change the Server IP to your own private server IP. ```yaml name: deploy on: push: workflow_dispatch: schedule: # Runs everyday at 8:00 AM - cron: \u0026quot;0 0 * * *\u0026quot; jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 with: submodules: true fetch-depth: 0 - name: Setup Hugo uses: peaceiris/actions-hugo@v2 with: hugo-version: \u0026quot;latest\u0026quot; - name: Build Web run: hugo - name: Deploy Web Self uses: easingthemes/ssh-deploy@main with: SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }} REMOTE_HOST: Server_IP REMOTE_USER: git SOURCE: ./public/ TARGET: /app/blog/hugo/ ARGS: -avz --delete --chown=git:git ``` HTTPS Configuration Here we use Alibaba Cloud\u0026rsquo;s free SSL certificate for HTTPS configuration. However, since the free certificate has a short validity period, we need to install acme for automatic renewal and updates.\nApply for Alibaba Cloud API\nCreate an acme access user\nAfter logging into the Alibaba Cloud backend, go to Resource Access Management (RAM). Select Identities / Users, create a new user, check OpenAPI Access (AccessKey), confirm, and save the corresponding key and secret for later use.\nConfigure permissions\nClick Add Permissions and grant the new user the following permissions:\nAliyunRAMFullAccess\nConfigure acme Automatic Updates\nInstall acme\n1 curl [https://get.acme.sh](https://get.acme.sh) | sh -s email=xxxx@xxxx.com Use the key and secret saved in step 1 to configure Alibaba Cloud\u0026rsquo;s system parameters:\n1 2 export Ali_Key=\u0026#34;key\u0026#34; export Ali_Secret=\u0026#34;Secret\u0026#34; Issue the certificate:\n1 acme.sh --issue --dns dns_ali -d zoyao.site -d \u0026#39;*.zoyao.site\u0026#39; --dnssleep 300 --debug 1 2 3 4 5 6 - Save the certificate. Because I deployed Nginx using Docker, I need to restart Nginx after pulling the certificate, so I added `--reloadcmd \u0026#34;docker restart nginx\u0026#34;`. You can delete or modify it to your own command based on actual needs. ```bash acme.sh --install-cert -d zoyao.site --key-file /app/nginx/ssl/zoyao.site/key.pem --fullchain-file /app/nginx/ssl/zoyao.site/cert.pem --reloadcmd \u0026#34;docker restart nginx\u0026#34; - Once successfully executed, you can view the certificate in the `/app/nginx/ssl/zoyao.site/` directory. Nginx SSL Configuration\nIn Nginx\u0026rsquo;s ./nginx/conf.d directory, create a new configuration file, named here as common.conf.\nIn the previous private deployment, we used port 8888 as the blog\u0026rsquo;s access port. Here we only need to forward the SSL 443 port to the 8888 port and configure the SSL certificate address. The configuration file is as follows:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 server { listen 443 ssl http2; listen [::]:443 ssl http2; # Configure blog domain name server_name www.zoyao.site zoyao.site; # The SSL certificate address saved in the previous step. Because it\u0026#39;s a Docker deployment, the mapped address is configured here ssl_certificate \u0026#34;/app/ssl/zoyao.site/cert.pem\u0026#34;; ssl_certificate_key \u0026#34;/app/ssl/zoyao.site/key.pem\u0026#34;; ssl_session_cache shared:SSL:1m; ssl_session_timeout 10m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { # Forward the request to the port where the blog is actually deployed proxy_pass [http://127.0.0.1:8888](http://127.0.0.1:8888); # Forwarding rule proxy_set_header Host $proxy_host; # Modify forwarding request header so the app on port 8080 can receive the real request proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } At this point, the HTTPS deployment is complete. Open https://www.zoyao.site to access it.\ni18n Internationalization Hugo provides a complete internationalization solution natively. You can define different language setups in hugo.yaml.\nHere, I have defined two languages: Chinese and English.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 languages: en: languageName: English title: zoyao’s Travel Diary weight: 1 params: sidebar: subtitle: Let\u0026#39;s bust our asses and meet again at the pinnacle of success. zh-cn: languageName: 中文 title: zoyao的旅行日记 weight: 2 params: sidebar: subtitle: 顶峰相见 Correspondingly, in the articles, you also need different Markdown files to distinguish between different languages.\nFor example, for this article, index.zh-cn.md is used for Chinese.\nIn the same directory, you can create index.en.md for English.\nYou can configure the website\u0026rsquo;s default language through DefaultContentLanguage:\n1 2 DefaultContentLanguage: zh-cn languageCode: zh-cn This website uses different default languages for different deployment methods. In zoyao.site, it defaults to Chinese, while in zoyao.github.io, it defaults to English. You just need to define the corresponding configuration in the config folder under the root directory.\n1 2 3 4 5 6 7 mysite ├── config.toml └── config ├── development │ └── config.toml └── production └── config.toml Different configuration files use a different DefaultContentLanguage.\nSimilarly, during automated deployment, you can specify different configuration files to complete the deployment for the corresponding language environment:\n1 2 - name: Build Web run: hugo --environment production ","date":"2025-06-03T00:00:00Z","image":"https://zoyao.site/p/hugo-%E5%8D%9A%E5%AE%A2-github-page-/%E7%A7%81%E6%9C%89%E5%8C%96-%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97/hugo-logo-wide.svg","permalink":"https://zoyao.site/en/p/hugo-blog-github-pages-/-private-deployment-guide/","title":"Hugo Blog Github Pages / Private Deployment Guide"},{"content":"This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme.\nHeadings The following HTML \u0026lt;h1\u0026gt;—\u0026lt;h6\u0026gt; elements represent six levels of section headings. \u0026lt;h1\u0026gt; is the highest section level while \u0026lt;h6\u0026gt; is the lowest.\nH1 H2 H3 H4 H5 H6 Paragraph Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.\nItatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.\nBlockquotes The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a footer or cite element, and optionally with in-line changes such as annotations and abbreviations.\nBlockquote without attribution Tiam, ad mint andaepu dandae nostion secatur sequo quae. Note that you can use Markdown syntax within a blockquote.\nBlockquote with attribution Don\u0026rsquo;t communicate by sharing memory, share memory by communicating.\n— Rob Pike1\nTables Tables aren\u0026rsquo;t part of the core Markdown spec, but Hugo supports supports them out-of-the-box.\nName Age Bob 27 Alice 23 Inline Markdown within tables Italics Bold Code italics bold code A B C D E F Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ultricies, sapien non euismod aliquam, dui ligula tincidunt odio, at accumsan nulla sapien eget ex. Proin eleifend dictum ipsum, non euismod ipsum pulvinar et. Vivamus sollicitudin, quam in pulvinar aliquam, metus elit pretium purus Proin sit amet velit nec enim imperdiet vehicula. Ut bibendum vestibulum quam, eu egestas turpis gravida nec Sed scelerisque nec turpis vel viverra. Vivamus vitae pretium sapien Code Blocks Code block with backticks 1 2 3 4 5 6 7 8 9 10 \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Code block indented with four spaces \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026quot;en\u0026quot;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026quot;utf-8\u0026quot;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Code block with Hugo\u0026rsquo;s internal highlight shortcode 1 2 3 4 5 6 7 8 9 10 \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Diff code block 1 2 3 4 5 [dependencies.bevy] git = \u0026#34;https://github.com/bevyengine/bevy\u0026#34; rev = \u0026#34;11f52b8c72fc3a568e8bb4a4cd1f3eb025ac2e13\u0026#34; - features = [\u0026#34;dynamic\u0026#34;] + features = [\u0026#34;jpeg\u0026#34;, \u0026#34;dynamic\u0026#34;] List Types Ordered List First item Second item Third item Unordered List List item Another item And another item Nested list Fruit Apple Orange Banana Dairy Milk Cheese Other Elements — abbr, sub, sup, kbd, mark GIF is a bitmap image format.\nH2O\nXn + Yn = Zn\nPress CTRL + ALT + Delete to end the session.\nMost salamanders are nocturnal, and hunt for insects, worms, and other small creatures.\nHyperlinked image The above quote is excerpted from Rob Pike\u0026rsquo;s talk during Gopherfest, November 18, 2015.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2019-03-11T00:00:00Z","image":"https://zoyao.site/p/markdown-syntax-guide/pawel-czerwinski-8uZPynIu-rQ-unsplash_hu_961024150ee0dc47.jpg","permalink":"https://zoyao.site/en/p/markdown-syntax-guide/","title":"Markdown Syntax Guide"}]