This is an automatic translation generated by artificial intelligence. May contain errors.
In this guide we will explain how to deploy on Netlify a static site developed with Hugo, whose code is hosted in a GitLab repository and is currently deployed on GitLab Pages.
-
Install the preliminary material. In our case we will need the code editor Visual Studio Code (VS Code), the version control software Git, and Hugo.
-
Make a fork of the current repository and clone it to our local machine. Then, open the root directory in VS Code to make all the changes described in the following steps.
-
Create the netlify.toml file in the project root, taking into account our Hugo version, the name of the folder where Hugo-generated files will be stored (publish), and the command Netlify will use to deploy the site (command). In our case the file would be:
[build.environment]
HUGO_VERSION = "0.123.7"
[build]
publish = "public"
command = "hugo"
- Modify the file static/admin/index.html, deleting its content and copying the following:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Content Manager</title>
<!-- the script for authentication using Netlify Identity -->
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
</head>
<body>
<!-- the script that builds the page and powers Netlify CMS -->
<script src="https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"></script>
</body>
</html>
This code is the entry point for Netlify CMS (an open-source content management system for editing static sites, offering editors a user-friendly interface and Git workflows) on our site. Specifically, this file enables Netlify Identity, the authentication system that will be used to access Netlify CMS, and then loads Netlify CMS from the unpkg CDN. Once authenticated, the user can create, edit, and publish content within the CMS.
- Modify the backend section of the file static/admin/config.yml, changing
backend:
name: gitlab
repo: ofilibre/ofilibre.gitlab.io
branch: master # Branch to update (optional; defaults to master)
api_root: https://gitlab.com/api/v4
base_url: https://gitlab.com
auth_type: pkce
app_id: ba44978a94e18763a29c4f3f365d09e9d6e5b4fdbbb02c271ba13d0cbf0694d5
auth_endpoint: oauth/authorize
to
backend:
name: git-gateway
branch: master
This section defines the backend configuration, i.e., how and where changes are stored when users edit content in Netlify CMS. As for the name and branch fields, their role will be explained in step 8.
- Deploy the Project on Netlify. Once logged in to Netlify we follow these steps:
-
Select “Add New Site” and then “Import an existing project”.
-
In the Git provider selector, choose GitLab.
-
Select the repository and the appropriate Team.
-
Configure the sitename as
OfiLibre.
-
Select the correct branch (in our case, master).
- Modify the config.toml file. Returning to the project, access the config.toml file (located in the root) and change the baseURL value to the URL provided by Netlify (based on the sitename). In our case:
baseURL = “https://ofilibre.netlify.app”
Save the changes and update the repository (push). Once the changes are reflected (approximately two minutes) we should be able to access our website using the URL https://ofilibre.netlify.app.
- Configure Netlify Identity. Once we have accessed the site administration panel in Netlify, go to the Identity section and select “Enable Identity” to activate Netlify Identity. Once enabled, configure the following options as needed:
-
Invite Users: If we need to invite users to the site.
-
External Providers: If we want to authenticate through an external provider (such as Google or Gitlab). In our case it was necessary to use an external provider because we had errors creating the Netlify Identity account, which is the default authentication method.
-
Enable Git Gateway: Enable this option to allow integration with GitLab.
- Configure an Access Token. Personal Access Tokens in GitLab are authentication keys used to interact with the GitLab API or perform Git operations without using your username and password.
Git-Gateway tokens in Netlify are keys generated to allow a user (usually through an interface like Netlify CMS) to interact with a Git repository without needing direct access to the Git provider’s API, i.e., Git-Gateway acts as an intermediary between Netlify CMS and the Git provider. When you log in to Netlify CMS, Git-Gateway authenticates the user and manages requests to the Git repository on your behalf on the branch (branch) indicated in the backend section (step 4).
GitLab version 15.0 removed support for non-expiring OAuth tokens, meaning any previous token (without expiration) became invalid, and OAuth tokens now have a two-hour expiration date.
When Netlify Identity is enabled, we are provided with an OAuth token, so after two hours we will no longer be able to access Netlify CMS. The solution is to create a Gitlab personal access token:
-
Access our GitLab profile, specifically the Edit profile section.
-
Select the Access tokens option and then Add new token.
-
Configure the name, description, and expiration date of the token.
-
Configure the token permissions, ensuring that the api, read_api, read_repository, and write_repository options are enabled.
-
Copy the token and go to the Netlify configuration.
-
Paste the GitLab Personal Access Token into the Netlify Identity configuration to allow authentication between Netlify and GitLab.
-
Test Netlify CMS. The current configuration allows publishing and editing of blogs and guides.
-
Modify the file static/admin/config.yml, to allow editing and publishing of sheets and presentations, adding the corresponding fields in each collection. Additionally, we modify the management of attachments through the media_folder and public_folder attributes. The resulting collections section would be:
collections:
- name: 'blog'
label: 'Blog'
folder: 'content/espanol/blog'
create: true
slug: '{{year}}-{{month}}-{{day}}-{{slug}}'
media_folder: '/../../../static/blog/{{slug}}'
public_folder: '/blog/{{slug}}'
editor:
preview: true
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publication date', name: 'date', widget: 'date' }
- { label: 'Description', name: 'description', widget: 'string' }
- { label: 'Type', name: 'type', widget: 'hidden', default: 'post'}
- { label: 'Categories', name: 'categories', widget: 'hidden', default: ["OfiLibre"]}
- { label: 'Tags', name: 'tags', widget: 'list'}
- { label: 'Background (elongated)', name: 'bg_image', widget: 'image', default: '/images/backgrounds/page-title.jpg'}
- { label: 'Logo (square)', name: 'thumb', widget: 'image', default: '/images/logo-ofilibre-2025.jpg'}
- { label: 'Body', name: 'body', widget: 'markdown' }
- label: 'Attachments'
name: 'files'
widget: 'list'
media_folder: '/../../../static/documentos/{{slug}}'
public_folder: '/documentos/{{slug}}'
fields:
- { label: 'File', name: 'file', widget: 'file' }
- name: 'guias'
label: 'Guides'
folder: 'content/espanol/guias'
create: true
slug: '{{slug}}'
media_folder: '/../../../static/images/guias/{{slug}}'
public_folder: '../../images/guias/{{slug}}'
editor:
preview: true
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publication date', name: 'date', widget: 'date' }
- { label: 'Description', name: 'description', widget: 'string' }
- { label: 'Logo', name: 'logo', widget: 'image' }
- { label: 'Background', name: 'feature', widget: 'image', required: false }
- { label: 'Categories', name: 'categories', widget: 'hidden', default: ["OfiLibre"]}
- { label: 'Tags', name: 'tags', widget: 'list'}
- { label: 'Type', name: 'type', widget: 'hidden', default: 'guias'}
- { label: 'Body', name: 'body', widget: 'markdown' }
- label: 'Attachments'
name: 'files'
widget: 'list'
media_folder: '/../../../static/documentos/{{slug}}'
public_folder: '/documentos/{{slug}}'
fields:
- { label: 'File', name: 'file', widget: 'file' }
- name: 'fichas'
label: 'Sheets'
folder: 'content/espanol/fichas'
create: true
slug: '{{slug}}'
media_folder: '/../../../static/images/fichas/{{slug}}'
public_folder: ''
editor:
preview: true
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publication date', name: 'date', widget: 'date' }
- { label: 'Logo', name: 'logo', widget: 'image' }
- { label: 'Background', name: 'feature', widget: 'image', required: false }
- { label: 'Website', name: 'website', widget: 'string' }
- { label: 'Website (Spanish)', name: 'website_es', widget: 'string', required: false }
- label: 'Licenses'
name: 'licenses'
widget: 'list'
fields:
- { label: 'Name', name: 'name', widget: 'string' }
- { label: 'URL', name: 'url', widget: 'string' }
- { label: 'MyApps', name: 'myapps', widget: 'boolean', default: false }
- { label: 'Source code (URL)', name: 'source', widget: 'string' }
- label: 'Installation guides'
name: 'installs'
widget: 'list'
fields:
- { label: 'Name', name: 'name', widget: 'string' }
- { label: 'URL', name: 'url', widget: 'string' }
- { label: 'Format', name: 'kind', widget: 'string', required: false }
- label: 'Tutorials'
name: 'tutorials'
widget: 'list'
fields:
- { label: 'Name', name: 'name', widget: 'string' }
- { label: 'URL', name: 'url', widget: 'string' }
- { label: 'Format', name: 'kind', widget: 'string', required: false }
- label: 'Extras'
name: 'others'
widget: 'list'
required: false
fields:
- { label: 'Name', name: 'name', widget: 'string' }
- { label: 'URL', name: 'url', widget: 'string' }
- label: 'Screenshots'
name: 'screenshots'
widget: 'list'
fields:
- { label: 'Name', name: 'name', widget: 'string' }
- { label: 'File', name: 'file', widget: 'image' }
- { label: 'Attribution', name: 'attribution', widget: 'string', required: false }
- { label: 'Type', name: 'type', widget: 'hidden', default: 'fichas' }
- { label: 'Body', name: 'body', widget: 'markdown' }
- label: 'Attachments'
name: 'files'
widget: 'list'
media_folder: '/../../../static/documentos/{{slug}}'
public_folder: '/documentos/{{slug}}'
fields:
- { label: 'File', name: 'file', widget: 'file' }
- name: 'pres'
label: 'Presentations'
folder: 'content/espanol/pres'
create: true
slug: '{{slug}}'
media_folder: '/../../../static/pres/{{slug}}'
public_folder: ''
editor:
preview: true
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publication date', name: 'date', widget: 'date' }
- { label: 'Description', name: 'description', widget: 'string' }
- { label: 'Featured image', name: 'feature', widget: 'image' }
- { label: 'Teaser image', name: 'teaser', widget: 'image' }
- label: 'Slides'
name: 'transpas'
widget: 'object'
fields:
- { label: 'PDF file', name: 'pdf', widget: 'file' }
- { label: 'ODP file', name: 'odp', widget: 'file' }
- label: 'Extras'
name: 'extras'
required: false
widget: 'list'
fields:
- { label: 'Name', name: 'name', widget: 'string' }
- { label: 'URL', name: 'url', widget: 'string' }
- { label: 'Type', name: 'type', widget: 'hidden', default: 'pres' }
- { label: 'Body', name: 'body', widget: 'markdown' }
- DNS and domain configuration
-
Add the custom domain in Netlify (Domain management -> Add a domain -> ofilibre.urjc.es), which the university owns.
-
In this step we choose whether to use Netlify DNS as the DNS provider or continue with our current provider. In our case we chose the second option, so we must inform the provider of the changes to be made to the DNS records so that our domain points to the site deployed on Netlify.
-
Following Netlify’s documentation, since we want to register a primary domain (apex domain), we inform our DNS provider to look up the DNS record configuration for ofilibre.urjc.es and add an ALIAS record with the host field equal to ‘@’ and pointing to
apex-loadbalancer.netlify.com.
Changes may take up to 48 hours to be reflected. -
Request an SSL certificate through the Harica certificate management interface. To do this, go to the ‘Server’ section within the interface and fill in the fields, such as FQDN (in our case ofilibre.urjc.es), certificate type (SSL DV), and algorithm (RSA 2048). Additionally, select the option to generate a private key.
-
When the certificate is approved, we will be provided with a series of files, including the certificate itself and the intermediate certificate chain. Using these two files and the private key from the previous step, add the certificate to Netlify (Domain management -> HTTPS -> Set custom certificate) to enable https.
- Modify the config.toml file and check the DNS change. Returning to the project, access the config.toml file and change the baseURL value to the final URL, i.e.:
baseURL = “https://ofilibre.urjc.es”
Finally, check that the DNS change has been registered correctly by clearing the cache (sudo resolvectl flush-caches on Linux) and using the dig command (dig ofilibre.urjc.es) to see that the domain indeed points to apex-loadbalancer.netlify.com.