{
    "componentChunkName": "component---src-templates-docs-js",
    "path": "/veckoanvisningar/kursvecka-03/cumulus/your-first-cumulus-virtual-personal-server",
    "result": {"data":{"site":{"siteMetadata":{"title":"Linnéuniversitetet","language":"sv"}},"mdx":{"fields":{"id":"5c9f0212-55a6-57b9-97f6-eebb75ca10f6","title":"Your First Cumulus Virtual Personal Server","slug":"/veckoanvisningar/kursvecka-03/cumulus/your-first-cumulus-virtual-personal-server"},"body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"Your First Cumulus Virtual Personal Server\",\n  \"metaTitle\": \"1DV026 | Your First Cumulus Virtual Personal Server\",\n  \"metaDescription\": \"metaDescription\"\n};\n\nvar makeShortcode = function makeShortcode(name) {\n  return function MDXDefaultShortcode(props) {\n    console.warn(\"Component \" + name + \" was not imported, exported, or provided by MDXProvider as global scope\");\n    return mdx(\"div\", props);\n  };\n};\n\nvar Hint = makeShortcode(\"Hint\");\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, _excluded);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(Hint, {\n    type: \"warning\",\n    mdxType: \"Hint\"\n  }, \"Your Cumulus server can serve content to the Internet when you set up web services, but server access requires VPN connection. **WARNING: This server must only be used for course activities. Unauthorized use for private projects will result in immediate loss of access and potential disciplinary action.**\"), mdx(Hint, {\n    type: \"warning\",\n    mdxType: \"Hint\"\n  }, \"The SSH key you have received should be considered a secret. Never share it with third parties or display it publicly.\"), mdx(\"h2\", null, \"What is Cumulus?\"), mdx(\"p\", null, \"Cumulus is Linnaeus University's cloud computing platform that provides students\\nwith virtual personal servers (VPS) for course-related projects and exercises. Each\\nstudent receives:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"A personal Ubuntu Linux server\"), \" running in the cloud\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Root-level access\"), \" to install software and configure services\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Public internet connectivity\"), \" with a unique DNS name (e.g., \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"cuXXXX.camp.lnu.se\"), \")\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"SSH access\"), \" through the university's VPN network\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"What you can do with your Cumulus server:\")), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Deploy web applications and APIs\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Learn Linux system administration\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Practice server configuration and security\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Host course projects accessible from anywhere on the internet\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Experiment with different technologies without affecting your personal computer\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Important limitations:\")), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Access requires connection to the university VPN\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The server is for \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"course-related activities only\"), \" - not personal projects\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Your server and data may be reset between courses\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Resource usage is monitored and limited\")), mdx(\"p\", null, \"Think of Cumulus as your personal sandbox server where you can safely learn and\\nexperiment with real-world server technologies in a controlled environment.\"), mdx(\"h2\", null, \"Step 1: Verify SSH Availability\"), mdx(\"p\", null, \"Before you can connect to your Cumulus server, make sure that the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"ssh\"), \" command\\nis available in your terminal.\"), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"Windows Instructions\")), mdx(\"p\", null, \"If you're using Windows 10 or later, the built-in PowerShell or Command Prompt\\nusually includes SSH, but we recommend using \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Git Bash\"), \" as your terminal (this\\nguide assumes you're using a Bash terminal).\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Check if SSH is installed:\")), mdx(\"p\", null, \"Open Git Bash (or your preferred terminal) and run:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"ssh -V\\n\")), mdx(\"p\", null, \"If you see a version number (e.g., \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"OpenSSH_9.9p1, OpenSSL 3.2.3 3 Sep 2024\"), \"),\\nyou're good to go and can proceed to Step 2.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"If SSH is not available, you have several options:\")), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Install OpenSSH Client\"), \" via Windows Features (Settings \\u2192 Apps \\u2192 Optional Features)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Use Windows Terminal\"), \" with PowerShell or Command Prompt\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Install Windows Subsystem for Linux (WSL)\"), \" - provides a complete Linux\\nenvironment with native SSH support\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Alternatively, install PuTTY\"), \", though this guide assumes OpenSSH command-line\\ntools\"))), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"macOS Instructions\")), mdx(\"p\", null, \"SSH is pre-installed on macOS. You can verify it's available by opening Terminal\\nand running:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"ssh -V\\n\")), mdx(\"p\", null, \"If you see a version number, you're ready to proceed to Step 2.\")), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"Linux Instructions\")), mdx(\"p\", null, \"SSH is typically pre-installed on most Linux distributions. You can verify it's\\navailable by opening your terminal and running:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"ssh -V\\n\")), mdx(\"p\", null, \"If you see a version number, you're ready to proceed to Step 2.\"), mdx(\"p\", null, \"If SSH is not installed, you can install it using your distribution's package manager:\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Ubuntu/Debian:\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo apt install openssh-client\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Fedora:\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo dnf install openssh-clients\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Arch Linux:\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo pacman -S openssh\\n\"))), mdx(\"h2\", null, \"Step 2: Download Your Identity File from GitLab\"), mdx(\"p\", null, \"To access your personal server in the Cumulus cloud via SSH, you need an\\nidentity file (private key). This file is provided through a GitLab project.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Locate and download the identity file:\")), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Go to the \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Secrets project\"), \" in GitLab\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"In this project, you will find:\", mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Your \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"private key file\"), \" (named something like \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"id_cumulus_xxx\"), \")\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"IP address\"), \" of your server\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"DNS name\"), \" assigned to your server\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Download the private key file (\", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"id_cumulus_xxx\"), \") to a known location on your computer\")), mdx(\"p\", null, \"Keep this file safe - you'll need it in the next step.\"), mdx(\"h2\", null, \"Step 3: Create the .ssh Directory\"), mdx(\"p\", null, \"The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".ssh\"), \" directory is a standard location for storing SSH keys and configuration\\nfiles. If it doesn't already exist, you need to create it.\"), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"Windows (Git Bash) Instructions\")), mdx(\"p\", null, \"Open Git Bash and run:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"mkdir -p ~/.ssh\\n\")), mdx(\"p\", null, \"This creates the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".ssh\"), \" directory in your home folder if it doesn't already exist.\\nThe \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"-p\"), \" flag ensures no error occurs if the directory is already there.\")), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"macOS Instructions\")), mdx(\"p\", null, \"Open Terminal and run:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"mkdir -p ~/.ssh\\n\")), mdx(\"p\", null, \"This creates the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".ssh\"), \" directory in your home folder if it doesn't already exist.\\nThe \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"-p\"), \" flag ensures no error occurs if the directory is already there.\")), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"Linux Instructions\")), mdx(\"p\", null, \"Open your terminal and run:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"mkdir -p ~/.ssh\\n\")), mdx(\"p\", null, \"This creates the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".ssh\"), \" directory in your home folder if it doesn't already exist.\\nThe \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"-p\"), \" flag ensures no error occurs if the directory is already there.\")), mdx(\"h2\", null, \"Step 4: Move the Identity File to .ssh Directory\"), mdx(\"p\", null, \"Now you need to move the downloaded identity file to the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".ssh\"), \" directory.\"), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"Windows (Git Bash) Instructions\")), mdx(\"p\", null, \"In Git Bash, navigate to where you downloaded the file (e.g., Downloads folder)\\nand move it to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".ssh\"), \":\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"mv ~/Downloads/id_cumulus_xxx ~/.ssh/\\n\")), mdx(\"p\", null, \"Replace \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"id_cumulus_xxx\"), \" with the actual filename of your private key.\"), mdx(\"p\", null, \"If your file is in a different location, adjust the path accordingly:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"mv /c/path/to/id_cumulus_xxx ~/.ssh/\\n\"))), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"macOS Instructions\")), mdx(\"p\", null, \"In Terminal, move the file from your Downloads folder to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".ssh\"), \":\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"mv ~/Downloads/id_cumulus_xxx ~/.ssh/\\n\")), mdx(\"p\", null, \"Replace \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"id_cumulus_xxx\"), \" with the actual filename of your private key.\"), mdx(\"p\", null, \"If your file is in a different location, adjust the path accordingly.\")), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"Linux Instructions\")), mdx(\"p\", null, \"In your terminal, move the file from your Downloads folder to \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".ssh\"), \":\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"mv ~/Downloads/id_cumulus_xxx ~/.ssh/\\n\")), mdx(\"p\", null, \"Replace \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"id_cumulus_xxx\"), \" with the actual filename of your private key.\"), mdx(\"p\", null, \"If your file is in a different location, adjust the path accordingly.\")), mdx(\"h2\", null, \"Step 5: Set Correct Permissions on the Identity File\"), mdx(\"p\", null, \"Setting the correct permissions ensures that your private key is only readable\\nby you. SSH will refuse to use the key if it's accessible by others, as this\\nwould pose a security risk.\"), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"Windows (Git Bash) Instructions\")), mdx(\"p\", null, \"In Git Bash, run:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"chmod 600 ~/.ssh/id_cumulus_xxx\\n\")), mdx(\"p\", null, \"Replace \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"id_cumulus_xxx\"), \" with the actual filename of your private key.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Note:\"), \" When using Git Bash, \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"chmod 600\"), \" works as expected and ensures your\\nkey is accepted by SSH, even though Windows handles file permissions differently\\nunder the hood.\")), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"macOS Instructions\")), mdx(\"p\", null, \"In Terminal, run:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"chmod 600 ~/.ssh/id_cumulus_xxx\\n\")), mdx(\"p\", null, \"Replace \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"id_cumulus_xxx\"), \" with the actual filename of your private key.\"), mdx(\"p\", null, \"This command sets the file permissions so that only you (the owner) can read and\\nwrite the file, while removing all permissions for group and others.\")), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"Linux Instructions\")), mdx(\"p\", null, \"In your terminal, run:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"chmod 600 ~/.ssh/id_cumulus_xxx\\n\")), mdx(\"p\", null, \"Replace \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"id_cumulus_xxx\"), \" with the actual filename of your private key.\"), mdx(\"p\", null, \"This command sets the file permissions so that only you (the owner) can read and\\nwrite the file, while removing all permissions for group and others.\")), \"## Step 3: Connect to Your Cumulus Server via SSH\", mdx(\"p\", null, \"Once your identity file is in place and has the correct permissions, you can\\nconnect to your virtual personal server in the Cumulus cloud using SSH.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Use the DNS name\"), \" (or IP address)\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"In the GitLab Secrets project, you will find either:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The DNS name of your virtual personal server (recommended), e.g. cuXXXX.camp.lnu.se\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Or the IP address, e.g. 172.27.XXX.XXX\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Connect using SSH\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Open your terminal (Git Bash on Windows) and run:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"ssh -i ~/.ssh/id_cumulus_xxx ubuntu@cuXXXX.camp.lnu.se\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Replace:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"id_cumulus_xxx\"), \" with the actual filename of your private key\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"cuXXXX.camp.lnu.se\"), \" with your server\\u2019s DNS name (or IP address)\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"\\uD83D\\uDCCC Example\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"ssh -i ~/.ssh/id_cumulus_en999zz ubuntu@cu1151.campus.lnu.se\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"If this is your first time connecting, you may be asked to confirm the host's\\nfingerprint. Type yes to continue.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"\\uD83D\\uDD27 Troubleshooting: Remove a Previously Registered Key\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"If you\\u2019ve connected to your server before and the host key has changed (for\\nexample, due to a reinstallation), SSH may refuse to connect and show a warning\\nabout a mismatched key.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"To fix this, remove the old key from your known hosts file:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"ssh-keygen -R cuXXXX.camp.lnu.se\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Replace \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"cuXXXX.camp.lnu.se\"), \" with your servers\\u2019s actual DNS name or IP address.\\nThis command deletes the outdated entry from \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"~/.ssh/known_hosts\"), \", allowing\\nyou to reconnect without errors.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"What Happens When You Successfully Connect\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"If the SSH connection is successful, you\\u2019ll see a welcome message from the\\nUbuntu operating system, along with system information such as memory usage,\\nIP address, and login history.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You will land in your home directory, typically \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"/home/ubuntu\"), \", and the prompt\\nwill look something like:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"ubuntu@cuXXXX:~\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \" From here, you can start working on your virtual personal server \\u2014 installing\\npackages, running applications, or setting up web services\"))), mdx(\"h2\", null, \"Step 4: Update Package Lists\"), mdx(\"p\", null, \"Before installing any software on your Cumulus virtual personal server, it's good\\npractice to update the package lists. This ensures that you get the latest available\\nversions of packages from the Ubuntu repositories.\"), mdx(\"p\", null, \"You are logged in as a regular user (ubuntu), so administrative commands like\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"apt\"), \" \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"update\"), \" require \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"sudo\"), \".\"), mdx(\"p\", null, \"Run the following command:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo apt update\\n\")), mdx(\"p\", null, \"This command fetches the most recent information about available packages and\\nupdates, which is necessary before installing tools like \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Node.js\"), \", \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"npm\"), \",\\nand \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"nginx\"), \".\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"You only need to run this once before installing multiple packages. There's no\\nneed to repeat it before each installation unless a significant amount of time\\nhas passed or you've added new repositories.\")), mdx(\"h2\", null, \"Step 5: Install Node.js and npm\"), mdx(\"p\", null, \"To run JavaScript applications on your Cumulus server, you need to install\\nNode.js and its package manager npm.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Add the NodeSource Repository\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Start by adding the official NodeSource repository for Node.js version 22:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This command downloads and runs a setup script that configures your system to\\nuse the NodeSource repository.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Install Node.js\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Once the repository is added, install Node.js and npm:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo apt install nodejs -y\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"The \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"-y\"), \" flag automatically confirms the installation.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Verify the Installation\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"After installation, check that both Node.js and npm are available:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"node -v\\nnpm -v\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You should see version numbers printed in the terminal, confirming that the\\ninstallation was successful.\"))), mdx(\"h2\", null, \"Step 6: Install and Start nginx\"), mdx(\"p\", null, \"To serve web content from your Cumulus server, you need to install a web server.\\nWe use nginx, a lightweight and high-performance HTTP server.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Install nginx\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Run the following command to install nginx:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo apt install nginx -y\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This installs the nginx package and its dependencies.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Start the nginx Service\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Once installed, start the nginx service:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo systemctl start nginx\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This launches the web server.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Enable nginx to Start on Boot\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"To make sure nginx starts automatically when the server boots:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo systemctl enable nginx\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Check nginx Status\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You can verify that nginx is running with:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"systemctl status nginx\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You should see output indicating that the service is active (running).\"))), mdx(\"h2\", null, \"Step 7: Verify nginx in Your Browser\"), mdx(\"p\", null, \"Once nginx is installed and running, you can verify that it works by accessing\\nyour server in a web browser using HTTP.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Use the DNS Name\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Open a browser and enter your server\\u2019s DNS name in the address bar, using http:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"http://cuXXXX.camp.lnu.se\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Replace cuXXXX.camp.lnu.se with your actual DNS name.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Expected Result\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You should see the default \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Welcome to nginx!\"), \" page. This confirms that:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"nginx is installed correctly\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"the service is running\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"your server is reachable over the network\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"If you don\\u2019t see the page, double-check that:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"VPN is connected\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"nginx is running (\", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"systemctl status nginx\"), \")\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"You used the correct DNS name\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"What Happens If You Use HTTPS?\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"At this stage, your nginx server is only configured to serve content over HTTP.\\nIf you try to access your server using HTTPS, like:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"https://cuXXXX.camp.lnu.se\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"\\u2026you will likely see a browser warning or error such as:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\\"Your connection is not private\\\"\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\\"This site can\\u2019t provide a secure connection\\\"\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\\"ERR_SSL_PROTOCOL_ERROR\\\"\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This is expected, because no SSL/TLS certificate has been installed yet.\"), mdx(\"blockquote\", {\n    parentName: \"li\"\n  }, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"You will configure HTTPS and install certificates in the next step.\")))), mdx(\"h2\", null, \"Step 8: Request and Install a TLS Certificate from LNU FTK Campus CA\"), mdx(\"p\", null, \"To enable HTTPS on your virtual server in the Cumulus network, you can request a\\ncertificate using Certbot and the LNU FTK Campus CA.\"), mdx(\"ol\", {\n    \"start\": 0\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Prerequisites\")), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Your server has a floating IP address in the Cumulus campus network.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"You have sudo privileges.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Nginx is installed and running.\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Install the LNU FTK Campus Root CA Certificate\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Download and install the root certificate so that Certbot can validate the CA:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo wget https://ca.camp.lnu.se/LNU_FTK_Campus_Root_CA_2025.crt -O /usr/local/share/ca-certificates/LNU_FTK_Campus_Root_CA_2025.crt\\n\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo update-ca-certificates\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Verify that the certificate is installed:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"ls -l /etc/ssl/certs | grep LNU\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You should see that the system has created a hash-based symlink pointing to a\\n\", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".pem\"), \" file, and another symlink named \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"LNU_FTK_Campus_Root_CA_2025.pem\"), \" pointing\\nto the original \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".crt\"), \" file. This confirms that the certificate has been added\\nto the system trust store and is available for use by Certbot and other tools.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Install Certbot\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Install Certbot using Snap and make it available via /usr/bin/certbot:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo snap install --classic certbot\\nsudo ln -s /snap/bin/certbot /usr/bin/certbot\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This ensures that Certbot is available system-wide and can be used with\\nplugins like Nginx.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Request and Install the Certificate\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Use Certbot to request a certificate from the LNU FTK Campus CA using the\\nNginx plugin and the custom ACME endpoint. The command below automatically\\nderives the correct FQDN from the server's floating IP:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo REQUESTS_CA_BUNDLE=/etc/ssl/certs/LNU_FTK_Campus_Root_CA_2025.pem \\\\\\ncertbot run -n --nginx \\\\\\n--server https://ca.camp.lnu.se:9000/acme/acme/directory \\\\\\n--agree-tos -d \\\"$(curl -s http://169.254.169.254/2009-04-04/meta-data/public-ipv4 | \\\\\\nawk -F. '{printf \\\"cu%d%03d.camp.lnu.se\\\\n\\\", $3%10, $4}')\\\"\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This command:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Uses the LNU FTK Campus ACME endpoint.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Automatically converts your floating IP to the correct FQDN.\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Requests and installs the certificate via the Nginx plugin.\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Test and Restart nginx Configuration\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"After Certbot has modified your nginx configuration, always test that the\\nconfiguration is valid before proceeding:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo nginx -t\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You should see:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"nginx: the configuration file /etc/nginx/nginx.conf syntax is ok\\nnginx: configuration file /etc/nginx/nginx.conf test is successful\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"If the test is successful, restart nginx to apply the changes:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo systemctl restart nginx\\n\")))), mdx(\"h2\", null, \"Step 9: Configuring Automatic Certificate Renewal\"), mdx(\"p\", null, \"Certificates issued by the LNU FTK Campus Certificate Authority are \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"short-lived (6\\u20137 days)\"), \".\\nTherefore, \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"automatic renewal is mandatory\"), \" to avoid service interruptions.\"), mdx(\"p\", null, \"When Certbot is installed via \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"snap\"), \", automatic renewal is handled by a systemd timer. However,\\nbecause an \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"internal ACME CA\"), \" is used, additional configuration is required.\"), mdx(\"h3\", null, \"How Automatic Renewal Works\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Certbot is executed automatically by the systemd timer\\n\", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"snap.certbot.renew.timer\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The timer runs several times per day\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Renewal occurs automatically when the certificate is close to expiration\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"No cron jobs or manual scripts are required\")), mdx(\"h3\", null, \"1. Configure Certbot for the Campus CA (One-time setup)\"), mdx(\"p\", null, \"Certbot must be provided with:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"the campus Root CA bundle\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"the internal ACME server URL\")), mdx(\"p\", null, \"This is done using a \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"systemd drop-in configuration\"), \".\"), mdx(\"h4\", null, \"1. Create a drop-in directory\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo mkdir -p /etc/systemd/system/snap.certbot.renew.service.d\\n\")), mdx(\"h4\", null, \"2. Create the drop-in configuration file\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo tee /etc/systemd/system/snap.certbot.renew.service.d/override.conf > /dev/null <<EOF\\n[Service]\\nEnvironment=\\\"REQUESTS_CA_BUNDLE=/etc/ssl/certs/LNU_FTK_Campus_Root_CA_2025.pem\\\"\\nEnvironment=\\\"CERTBOT_SERVER=https://ca.camp.lnu.se:9000/acme/acme/directory\\\"\\nEOF\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Explanation:\")), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"REQUESTS_CA_BUNDLE\"), \"\\nEnsures Certbot trusts the LNU FTK Campus Root CA\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"CERTBOT_SERVER\"), \"\\nPoints Certbot to the campus ACME endpoint instead of Let\\u2019s Encrypt\")), mdx(\"h3\", null, \"2. Reload systemd and test renewal\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo systemctl daemon-reload\\nsudo systemctl start snap.certbot.renew.service\\n\")), mdx(\"p\", null, \"This runs the same renewal logic that the timer uses.\"), mdx(\"h3\", null, \"3. Verify automatic renewal is scheduled\"), mdx(\"p\", null, \"Check that the systemd timer is active:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"systemctl list-timers snap.certbot.renew.timer\\n\")), mdx(\"p\", null, \"You should see output similar to:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"NEXT                         UNIT\\nWed 2025-12-17 14:56:00 UTC  snap.certbot.renew.timer\\n\")), mdx(\"p\", null, \"This confirms that automatic renewal is enabled.\"), mdx(\"h3\", null, \"4. Verify renewal results\"), mdx(\"p\", null, \"To check certificate status at any time:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo certbot certificates\\n\")), mdx(\"p\", null, \"To verify that a renewal actually occurred:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"grep \\\"successfully renewed\\\" /var/log/letsencrypt/letsencrypt.log\\n\")), mdx(\"p\", null, \"If the certificate is not close to expiration, Certbot will only perform a check. This is expected behavior.\"), mdx(\"p\", null, \"\\u2705 \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Automatic renewal is now fully configured.\")), mdx(\"p\", null, \"No further action is required. The certificate will be renewed automatically before it expires.\"), mdx(\"h2\", null, \"Step 10: Trust the CA in Your Browser\"), mdx(\"p\", null, \"Before you can verify HTTPS access without security warnings, you need to install\\nthe Root CA certificate in your browser's trust store.\"), mdx(\"h3\", null, \"1. Download the Root CA\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Download \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://ca.camp.lnu.se/LNU_FTK_Campus_Root_CA_2025.crt\"\n  }, \"LNU_FTK_Campus_Root_CA_2025.crt\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Remember the download location (e.g., Downloads folder)\")), mdx(\"h3\", null, \"2. Import the Certificate\"), mdx(\"p\", null, \"Choose your operating system and browser:\"), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"\\uD83D\\uDCBB macOS\")), mdx(\"h4\", null, \"Google Chrome / Safari (uses macOS Keychain)\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Locate the downloaded \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"LNU_FTK_Campus_Root_CA_2025.crt\"), \" file\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Double-click it \\u2192 opens in \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Keychain Access\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"System\"), \" keychain when prompted\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Find \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"LNU FTK Campus Root CA 2025\"), \" in the list\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Double-click it \\u2192 expand \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Trust\"), \" section\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Set \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"When using this certificate\"), \" \\u2192 \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Always Trust\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Close the window (enter admin password if prompted)\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Note:\"), \" Both Chrome and Safari now trust this CA.\"), mdx(\"h4\", null, \"Mozilla Firefox (separate certificate store)\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Open Firefox\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Go to \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Settings\"), \" (\\u2699\\uFE0F)\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Navigate to \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Privacy & Security\"), \" \\u2192 \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Certificates\"), \" \\u2192 \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"View Certificates\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select the \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Authorities\"), \" tab\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Click \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Import\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select the downloaded Root CA file\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Check \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"\\\"Trust this CA to identify websites\\\"\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Click \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"OK\"))), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Note:\"), \" Firefox uses its own certificate store, independent of macOS Keychain.\")), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"\\uD83D\\uDCBB Windows\")), mdx(\"h4\", null, \"Google Chrome\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Open Chrome\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Navigate to: \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"chrome://certificate-manager/localcerts/usercerts\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select the \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Local certificates\"), \" tab\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Under \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Installed by you\"), \", click \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Import (Trusted Certificates)\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Browse to the downloaded Root CA file\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Click \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Open\"), \" \\u2192 Confirm the import\")), mdx(\"h4\", null, \"Mozilla Firefox\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Open Firefox\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Go to \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Settings\"), \" \\u2192 \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Privacy & Security\"), \" \\u2192 \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"View Certificates\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select the \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Authorities\"), \" tab\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Click \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Import\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Choose the downloaded Root CA file\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Check \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"\\\"Trust this CA to identify websites\\\"\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Click \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"OK\")))), mdx(\"details\", null, mdx(\"summary\", null, mdx(\"strong\", null, \"\\uD83D\\uDCBB Linux\")), mdx(\"h4\", null, \"Google Chrome\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Open Chrome\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Navigate to: \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"chrome://certificate-manager/localcerts/usercerts\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select the \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Local certificates\"), \" tab\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Under \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Installed by you\"), \", click \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Import (Trusted Certificates)\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Browse to the downloaded Root CA file\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Click \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Open\"), \" \\u2192 Confirm the import\")), mdx(\"h4\", null, \"Mozilla Firefox\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Open Firefox\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Go to \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Settings\"), \" \\u2192 \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Privacy & Security\"), \" \\u2192 \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"View Certificates\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Select the \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Authorities\"), \" tab\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Click \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Import\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Choose the downloaded Root CA file\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Check \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"\\\"Trust this CA to identify websites\\\"\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Click \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"OK\")))), mdx(\"h3\", null, \"3. Restart Your Browser\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Important:\"), \" Completely close and restart your browser for the changes to take\\neffect.\"), mdx(\"p\", null, \"\\u2705 \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Your browser now trusts the LNU FTK Campus CA and you're ready to verify\\nHTTPS access without security warnings.\")), mdx(\"h2\", null, \"Step 11: Verify HTTPS in Your Browser\"), mdx(\"p\", null, \"After installing the TLS certificate, you should verify that HTTPS is working\\ncorrectly on your nginx server.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Test HTTPS Access\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Open a browser and enter your server's DNS name using HTTPS:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"https://cuXXXX.camp.lnu.se\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Replace cuXXXX.camp.lnu.se with your actual DNS name.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Expected Result\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You should see:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The same \", mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Welcome to nginx!\"), \" page as before\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"A secure connection indicator (lock icon) in your browser's address bar\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"No security warnings or certificate errors\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This confirms that:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The TLS certificate is installed correctly\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"HTTPS is properly configured\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Your connection is encrypted and secure\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Troubleshooting HTTPS Issues\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"If you encounter problems:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Certificate errors\"), \": Verify the certificate installation with:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo certbot certificates\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Nginx configuration\"), \": Check that nginx is using the certificate:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo nginx -t\\nsudo systemctl restart nginx\\n\"))), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Browser cache\"), \": Try a hard refresh (Ctrl+F5) or test in an\\nincognito/private window\")))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"HTTP vs HTTPS Behavior\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Now that HTTPS is configured:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"HTTP requests\"), \" \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"http://cuXXXX.camp.lnu.se\"), \" should automatically\\nredirect to HTTPS\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"HTTPS requests\"), \" \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"https://cuXXXX.camp.lnu.se\"), \" should work directly with\\na secure connection\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This automatic redirection is configured by Certbot when it updates your\\nnginx configuration.\"))), mdx(\"h2\", null, \"Step 12: Secure nginx Configuration\"), mdx(\"p\", null, \"Now that HTTPS is working, it's time to enhance the security of your nginx\\nserver by modifying the configuration and removing unnecessary information disclosure.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Edit the nginx Configuration\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Open the main nginx configuration file:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo nano /etc/nginx/nginx.conf\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Hide Server Version Information\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Find the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"http\"), \" block in the configuration file. Look for the commented line:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-nginx\"\n  }, \"#server_tokens off;\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Uncomment this line by removing the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"#\"), \" at the beginning:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-nginx\"\n  }, \"http {\\n    # ...existing configuration...\\n\\n    # Uncomment the following line to hide nginx version information\\n    server_tokens off;\\n    \\n    # ...existing configuration...\\n}\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This prevents potential attackers from knowing which specific version of\\nnginx you're running.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Save and Exit\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"In nano:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Press \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"Ctrl + O\"), \" to save\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Press \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"Enter\"), \" to confirm\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Press \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"Ctrl + X\"), \" to exit\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Test the Configuration\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Before restarting nginx, always test that your configuration is valid:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo nginx -t\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You should see:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"nginx: the configuration file /etc/nginx/nginx.conf syntax is ok\\nnginx: configuration file /etc/nginx/nginx.conf test is successful\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Restart nginx\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Apply the changes by restarting the nginx service:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo systemctl restart nginx\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Verify the Security Enhancement\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Test that the version information is now hidden by checking the HTTP headers:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"curl -I https://cuXXXX.camp.lnu.se\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Replace \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"cuXXXX.camp.lnu.se\"), \" with your actual DNS name.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Before the change, you would see:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"Server: nginx/1.24.0 (Ubuntu)\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"After the change, you should only see:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"Server: nginx\\n\")))), mdx(\"p\", null, \"This simple security enhancement reduces information leakage that could be\\nuseful to potential attackers.\"), mdx(\"h2\", null, \"Step 13: Create Your Own Static Website\"), mdx(\"p\", null, \"Instead of using the default nginx page, let's create your own custom website\\nwith a dedicated directory structure.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Create a New Directory for Your Website\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Create a dedicated directory for your hello-world website:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo mkdir -p /var/www/hello-world\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Set Proper Ownership\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Change ownership to the ubuntu user so you can edit files without sudo:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo chown -R ubuntu:ubuntu /var/www/hello-world\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Create Your Custom index.html\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Create your hello-world homepage:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"nano /var/www/hello-world/index.html\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Add the following content:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-html\"\n  }, \"<!DOCTYPE html>\\n<html lang=\\\"en\\\">\\n<head>\\n    <meta charset=\\\"UTF-8\\\">\\n    <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1.0\\\">\\n    <title>My Cumulus Server - Hello World</title>\\n    <style>\\n        body { \\n            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; \\n            margin: 0; \\n            padding: 40px; \\n            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\\n            color: white;\\n            min-height: 100vh;\\n        }\\n        .container { \\n            background: rgba(255, 255, 255, 0.95); \\n            padding: 40px; \\n            border-radius: 12px; \\n            box-shadow: 0 8px 32px rgba(0,0,0,0.3);\\n            color: #333;\\n            max-width: 800px;\\n            margin: 0 auto;\\n        }\\n        h1 { \\n            color: #2c3e50; \\n            text-align: center;\\n            margin-bottom: 30px;\\n        }\\n        .info { \\n            background: #e8f4fd; \\n            padding: 20px; \\n            border-radius: 8px; \\n            margin: 20px 0;\\n            border-left: 4px solid #3498db;\\n        }\\n        .status {\\n            background: #d4edda;\\n            border-left: 4px solid #28a745;\\n        }\\n        .feature-list {\\n            display: grid;\\n            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\\n            gap: 20px;\\n            margin: 30px 0;\\n        }\\n        .feature {\\n            background: #f8f9fa;\\n            padding: 20px;\\n            border-radius: 8px;\\n            border: 1px solid #dee2e6;\\n        }\\n        .feature h3 {\\n            margin-top: 0;\\n            color: #495057;\\n        }\\n    </style>\\n</head>\\n<body>\\n    <div class=\\\"container\\\">\\n        <h1>\\uD83C\\uDF1F Welcome to My Cumulus Server!</h1>\\n        \\n        <p>This is my first custom static website running on nginx with HTTPS\\n           security.</p>\\n        \\n        <div class=\\\"info status\\\">\\n            <strong>\\uD83D\\uDE80 Server Status:</strong> \\u2705 Running and Secure<br>\\n            <strong>\\uD83D\\uDD12 Security:</strong> \\u2705 HTTPS Enabled<br>\\n            <strong>\\uD83C\\uDF10 Platform:</strong> Ubuntu Linux on Cumulus Cloud\\n        </div>\\n\\n        <div class=\\\"feature-list\\\">\\n            <div class=\\\"feature\\\">\\n                <h3>\\uD83D\\uDD27 Technologies Used</h3>\\n                <ul>\\n                    <li>Ubuntu Linux</li>\\n                    <li>nginx Web Server</li>\\n                    <li>SSL/TLS</li>\\n                    <li>HTML5 & CSS3</li>\\n                </ul>\\n            </div>\\n            \\n            <div class=\\\"feature\\\">\\n                <h3>\\uD83D\\uDCDA What I've Learned</h3>\\n                <ul>\\n                    <li>SSH Connection</li>\\n                    <li>Package Management</li>\\n                    <li>Web Server Setup</li>\\n                    <li>SSL Certificate Installation</li>\\n                </ul>\\n            </div>\\n        </div>\\n\\n        <div class=\\\"info\\\">\\n            <strong>\\uD83D\\uDCDD Note:</strong> This website is served from \\n            <code>/var/www/hello-world</code> and is completely separate from\\n            nginx default files.\\n        </div>\\n    </div>\\n</body>\\n</html>\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Create a New nginx Site Configuration\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Create a dedicated configuration file for your domain:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo nano /etc/nginx/sites-available/cuXXXX.camp.lnu.se\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Add the following configuration:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-nginx\"\n  }, \"# HTTP to HTTPS redirect\\nserver {\\n    listen 80;\\n    listen [::]:80;\\n    server_name cuXXXX.camp.lnu.se;\\n\\n    return 301 https://$host$request_uri;\\n}\\n\\n# HTTPS server with static content\\nserver {\\n    listen 443 ssl http2;\\n    listen [::]:443 ssl http2;\\n    server_name cuXXXX.camp.lnu.se;\\n\\n    # SSL configuration (managed by Certbot)\\n    ssl_certificate /etc/letsencrypt/live/cuXXXX.camp.lnu.se/fullchain.pem;\\n    ssl_certificate_key /etc/letsencrypt/live/cuXXXX.camp.lnu.se/privkey.pem;\\n    include /etc/letsencrypt/options-ssl-nginx.conf;\\n    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;\\n\\n    # Serve static hello-world website\\n    root /var/www/hello-world;\\n    index index.html index.htm;\\n\\n    location / {\\n        try_files $uri $uri/ =404;\\n    }\\n\\n    # Include location blocks for different applications\\n    include /etc/nginx/conf.d/locations/*.conf;\\n}\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Important:\"), \" Replace \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"cuXXXX.camp.lnu.se\"), \" with your actual DNS name in\\nboth the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"server_name\"), \" and SSL certificate paths.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Configuration Breakdown:\")), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"First server block (HTTP)\"), \": Redirects all HTTP traffic to HTTPS for security\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Second server block (HTTPS)\"), \": Handles encrypted connections on port 443\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"http2\"), \": Enables HTTP/2 protocol for faster page loading and better performance\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"SSL certificates\"), \": Points to the certificates installed by Certbot\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"root /var/www/hello-world\"), \": Sets the directory for static files\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"location /\"), \": Serves static files, returns 404 if not found\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"include locations/*.conf\"), \": Loads configurations for dynamic applications\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"HTTP/2 Benefits:\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"HTTP/2 is automatically enabled in your nginx configuration and provides:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Multiplexing\"), \" - Multiple requests over a single connection\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Header compression\"), \" - Reduced overhead for HTTP headers\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Server push\"), \" - Proactive resource delivery (if configured)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Binary protocol\"), \" - More efficient than HTTP/1.1 text-based protocol\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Create Directory for Application Configurations\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Create a dedicated directory structure for modular nginx configurations. This\\nallows you to manage each application separately:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo mkdir -p /etc/nginx/conf.d/locations\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This directory will store individual \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \".conf\"), \" files for each application you\\ndeploy, making it easy to add, remove, or modify applications without touching\\nthe main site configuration.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Disable the Default Site and Enable Your Hello-World Site\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Remove the default nginx site and enable your custom hello-world site:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo unlink /etc/nginx/sites-enabled/default\\nsudo ln -s /etc/nginx/sites-available/cuXXXX.camp.lnu.se /etc/nginx/sites-enabled/\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Test and Restart nginx Configuration\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Always test the configuration before applying changes:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo nginx -t\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"If the test is successful, restart nginx:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo systemctl restart nginx\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Visit Your Custom Hello-World Website\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Open your browser and navigate to:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"https://cuXXXX.camp.lnu.se\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Replace \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"cuXXXX.camp.lnu.se\"), \" with your actual DNS name.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You should now see your custom hello-world website instead of the default\\nnginx page!\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Verify HTTP/2 is Working\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You can confirm that HTTP/2 is properly configured using either of these methods:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Browser Developer Tools\"), \": Open Developer Tools (F12) \\u2192 Network tab \\u2192\\nRefresh the page \\u2192 Check the \\\"Protocol\\\" column (should show \\\"h2\\\")\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Command line\"), \": Test with curl from your terminal:\"), mdx(\"blockquote\", {\n    parentName: \"li\"\n  }, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Note\"), \": Your curl must support HTTP/2. Check with:\"), mdx(\"pre\", {\n    parentName: \"blockquote\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"curl --version\\n\")), mdx(\"ul\", {\n    parentName: \"blockquote\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Look for \\\"Features: ... HTTP2 ...\\\"\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"If \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"--http2\"), \" is not recognized, you can try to update curl\\n(Linux: apt install curl,\\nWindows: winget install curl or download from \", mdx(\"a\", {\n    parentName: \"li\",\n    \"href\": \"https://curl.se/windows/\"\n  }, \"https://curl.se/windows/\"), \".\"))), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"curl -IL --http2 https://cuXXXX.camp.lnu.se\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Look for \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"HTTP/2 200\"), \" in the response headers.\"))))), mdx(\"h2\", null, \"Step 14: Create and Deploy an Express Application\"), mdx(\"p\", null, \"Now let's create a dynamic web application using Node.js with Express and deploy\\nit alongside your static website.\"), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"\\uD83D\\uDCC1 What you'll build:\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"/var/www/\\n\\u251C\\u2500\\u2500 hello-world/                    # Static website (from Step 11)\\n\\u2502   \\u2514\\u2500\\u2500 index.html\\n\\u251C\\u2500\\u2500 example-hello-world-express/    # Express application (this step)\\n\\u2502   \\u251C\\u2500\\u2500 package.json\\n\\u2502   \\u251C\\u2500\\u2500 src/server.js\\n\\u2502   \\u2514\\u2500\\u2500 node_modules/\\n\\u2514\\u2500\\u2500 ecosystem.config.yml           # PM2 configuration (Step 13)\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"\\uD83C\\uDF10 URL structure:\")), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"https://cuXXXX.camp.lnu.se/\"), \" \\u2192 Static website\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"https://cuXXXX.camp.lnu.se/hello-world-express/\"), \" \\u2192 Express application\")), mdx(\"h3\", null, \"Option A: Clone from GitLab (Recommended)\"), mdx(\"p\", null, \"If you have access to the course GitLab repository with the Express Hello World project:\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Set Up GitLab SSH Access on Your Cumulus Server (First Time Only)\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"To clone repositories from GitLab directly on your Cumulus server, you need\\nto configure SSH access. This creates a separate key pair specifically for your\\nCumulus server to access GitLab repositories.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"\\u26A0\\uFE0F Important:\"), \" These commands are run on your Cumulus server (via SSH),\\nnot on your local computer.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Generate a new SSH key pair for GitLab on your Cumulus server:\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"ssh-keygen -t ed25519 -C \\\"your.email@student.lnu.se\\\" -f ~/.ssh/id_gitlab\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"When prompted:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Press Enter to save in the default location (\", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"/home/ubuntu/.ssh/id_gitlab\"), \")\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Enter a passphrase (optional but recommended)\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Display the public key from your Cumulus server:\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"cat ~/.ssh/id_gitlab.pub\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Add the public key to your GitLab account:\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Copy the entire output from the command above and add it to your GitLab account:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Go to GitLab \\u2192 User Settings \\u2192 SSH Keys\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Paste the public key content\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Give it a descriptive title (e.g., \\\"Cumulus Server cuXXXX\\\")\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Click \\\"Add key\\\"\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Configure SSH on your Cumulus server to use the GitLab key:\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"nano ~/.ssh/config\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Add the following configuration:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"Host gitlab.lnu.se\\n    HostName gitlab.lnu.se\\n    User git\\n    IdentityFile ~/.ssh/id_gitlab\\n    IdentitiesOnly yes\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Test the connection from your Cumulus server:\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"ssh -T git@gitlab.lnu.se\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You should see: \\\"Welcome to GitLab, @yourusername!\\\"\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"What you've created:\")), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"A separate SSH key pair on your Cumulus server for GitLab access\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"SSH configuration that uses this key automatically for GitLab\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Secure connection between your Cumulus server and GitLab repositories\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Navigate to your home directory\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"cd ~\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Clone the Express Hello World Project\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You'll use a two-step process: clone in home directory, then move to the web directory:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"# Step 1: Clone to home directory (ubuntu user has full access here)\\ngit clone git@gitlab.lnu.se:1dv026/content/examples/example-hello-world-express.git\\n\\n# Step 2: Move to web directory with proper permissions\\nsudo mv example-hello-world-express /var/www/\\nsudo chown -R ubuntu:ubuntu /var/www/example-hello-world-express\\ncd /var/www/example-hello-world-express\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"\\uD83D\\uDCA1 Why you don't clone directly to \", mdx(\"inlineCode\", {\n    parentName: \"strong\"\n  }, \"/var/www/\"), \":\")), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\u274C \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"sudo git clone\"), \" \\u2192 SSH uses root's keys (which don't exist)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\u274C Direct clone to \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"/var/www/\"), \" \\u2192 Permission denied (directory owned by root)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\u2705 Clone to home \\u2192 move \\u2192 fix ownership = Clean and simple solution\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Install Dependencies\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"npm install\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Test the Application\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"npm start\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You should see: \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"\\uD83D\\uDE80 Server is running on http://localhost:3000\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Test locally: \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"curl http://localhost:3000\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Stop the server\"), \" with \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Ctrl + C\"), \" before proceeding to nginx configuration.\"))), mdx(\"h3\", null, \"Option B: Create from Scratch (Fallback)\"), mdx(\"p\", null, \"If you don't have access to the GitLab repository, create the application manually:\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Create Project Directory\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo mkdir -p /var/www/example-hello-world-express\\nsudo chown -R ubuntu:ubuntu /var/www/example-hello-world-express\\ncd /var/www/example-hello-world-express\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Create package.json\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"nano package.json\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Add the following content:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-json\"\n  }, \"{\\n  \\\"name\\\": \\\"example-hello-world-express\\\",\\n  \\\"version\\\": \\\"1.0.0\\\",\\n  \\\"description\\\": \\\"Express 5 Hello World with intentional plot twists \\uD83C\\uDFA2 - Teaching deployment the hard way!\\\",\\n  \\\"main\\\": \\\"src/server.js\\\",\\n  \\\"type\\\": \\\"module\\\",\\n  \\\"scripts\\\": {\\n    \\\"start\\\": \\\"node src/server.js\\\"\\n  },\\n  \\\"dependencies\\\": {\\n    \\\"express\\\": \\\"^5.0.0\\\"\\n  },\\n  \\\"author\\\": \\\"Your Name\\\",\\n  \\\"license\\\": \\\"MIT\\\"\\n}\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Create Source Directory and Server File\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"mkdir src\\nnano src/server.js\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Add the following Express application code:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-javascript\"\n  }, \"import express from 'express'\\n\\nconst PORT = process.env.PORT || 3000\\n\\nconst app = express()\\n\\n// Root route\\napp.get('/', (req, res) => {\\n  res.send('Hello World! (from Express)')\\n});\\n\\n// Test routes for error handling\\napp.get('/test/throw-error', (req, res) => {\\n  throw new Error('Test error')\\n});\\n\\napp.get('/test/crash', (req, res) => {\\n  process.exit(1)\\n});\\n\\n// Start server\\napp.listen(PORT, () => {\\n  console.log(`\\uD83D\\uDE80 Server is running on http://localhost:${PORT}`)\\n  console.log('\\uD83C\\uDF89 Ready to serve some spectacular Hello World content!')\\n  console.log('\\uD83D\\uDCA1 Pro tip: Try visiting /test-crash if you like living dangerously')\\n})\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Install Dependencies\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"npm install\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Test the Application\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"npm start\\n\")))), mdx(\"h3\", null, \"Configure nginx as Reverse Proxy\"), mdx(\"p\", null, \"Regardless of which option you used above, now configure nginx to serve your\\nExpress app using the modular configuration approach:\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Create Express App Location Configuration\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Instead of modifying the main nginx file, create a separate configuration for\\nthe Express app:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo nano /etc/nginx/conf.d/locations/hello-world-express.conf\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Add the following configuration:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-nginx\"\n  }, \"# Hello World Express application\\nlocation /hello-world-express/ {\\n    proxy_pass http://localhost:3000/;\\n    proxy_http_version 1.1;\\n    proxy_set_header Upgrade $http_upgrade;\\n    proxy_set_header Connection 'upgrade';\\n    proxy_set_header Host $host;\\n    proxy_set_header X-Real-IP $remote_addr;\\n    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\\n    proxy_set_header X-Forwarded-Proto $scheme;\\n}\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Test and Restart nginx Configuration\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Test the configuration and restart nginx:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo nginx -t\\nsudo systemctl restart nginx\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"No need to modify symlinks since we're using the include directive from Step 11.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Start Your Express Application\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"cd /var/www/example-hello-world-express\\nnpm start\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Test Your Setup\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Now you can access:\"), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Static website\"), \": \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"https://cuXXXX.camp.lnu.se/\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Express app\"), \": \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"https://cuXXXX.camp.lnu.se/hello-world-express/\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Test error handling\"), \": \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"https://cuXXXX.camp.lnu.se/hello-world-express/test/throw-error\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Test crash\"), \" (dangerous!): \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"https://cuXXXX.camp.lnu.se/hello-world-express/test/crash\"))), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Important:\"), \" Replace \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"cuXXXX.camp.lnu.se\"), \" with your actual DNS name.\"))), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"What You've Accomplished:\")), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Deployed an Express application with ES6 modules\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Configured nginx as a reverse proxy\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Set up both static and dynamic content on the same domain\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Added test routes with intentional errors and crashes\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Critical Discovery:\")), mdx(\"p\", null, \"Notice that when you close your terminal or visit \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"/hello-world-express/test/crash\"), \",\\nyour Express application stops working completely! This demonstrates a fundamental\\nproblem with running Node.js applications directly - they don't survive crashes\\nor disconnections.\"), mdx(\"p\", null, \"This is why we need a \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"process manager\"), \" in the next step!\"), mdx(\"h2\", null, \"Step 15: Install and Configure PM2 Process Manager\"), mdx(\"p\", null, \"As you discovered in the previous step, Node.js applications stop running when\\nyou close your terminal or when they crash. PM2 (Process Manager 2) solves this\\nproblem by keeping your applications running in the background, automatically\\nrestarting them on crashes, and providing process monitoring.\"), mdx(\"p\", null, \"Instead of managing each application separately, we'll use a YAML configuration\\nfile to define all our applications in one place.\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Install PM2 using npm\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Install PM2 globally so it's available system-wide:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo npm install -g pm2\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This installs PM2 as a global package, making the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"pm2\"), \" command available\\nfrom anywhere on your system.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Create the Ecosystem Configuration File\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Navigate to your web directory and create a PM2 ecosystem file:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"cd /var/www\\nsudo touch ecosystem.config.yml\\nsudo chown ubuntu:ubuntu ecosystem.config.yml\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Configure Your Applications\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Edit the ecosystem configuration file:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"nano ecosystem.config.yml\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Add the following YAML configuration:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-yaml\"\n  }, \"# PM2 Ecosystem Configuration\\n# This file defines all Node.js applications to be managed by PM2\\n\\napps:\\n  # Express Hello World Application\\n  - name: \\\"hello-world-express\\\"\\n    script: \\\"src/server.js\\\"\\n    cwd: \\\"/var/www/example-hello-world-express\\\"\\n    instances: 1\\n    exec_mode: \\\"fork\\\"\\n    watch: false\\n    env:\\n      NODE_ENV: \\\"production\\\"\\n      PORT: 3000\\n\\n  # Future applications can be added here\\n  # - name: \\\"my-api-server\\\"\\n  #   script: \\\"app.js\\\"\\n  #   cwd: \\\"/var/www/my-api-server\\\"\\n  #   instances: 1\\n  #   env:\\n  #     NODE_ENV: \\\"production\\\"\\n  #     PORT: 4000\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Configuration notes:\")), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"script: \\\"src/server.js\\\"\"), \" - Direct path to the Node.js application file\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"cwd\"), \" - Working directory for the application\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"exec_mode: \\\"fork\\\"\"), \" - Runs a single Node.js process (vs \\\"cluster\\\" for\\nmultiple processes)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"instances: 1\"), \" - Number of processes to run\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"env\"), \" - Environment variables for production mode\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Start All Applications with PM2 Using the Ecosystem File\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Use PM2 to start all applications defined in your ecosystem file:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"pm2 start ecosystem.config.yml\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"PM2 will read the configuration and start your Express application.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Verify Applications are Running\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Check the status of your PM2-managed applications:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"pm2 status\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You should see output similar to:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"\\u250C\\u2500\\u2500\\u2500\\u2500\\u2500\\u252C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u252C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u252C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u252C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u252C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u252C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u252C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u252C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u252C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u252C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u252C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u252C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2510\\n\\u2502 id  \\u2502 name                \\u2502 namespace   \\u2502 version \\u2502 mode    \\u2502 pid      \\u2502 uptime \\u2502 \\u21BA    \\u2502 status    \\u2502 cpu      \\u2502 mem      \\u2502 user     \\u2502 watching \\u2502\\n\\u251C\\u2500\\u2500\\u2500\\u2500\\u2500\\u253C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u253C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u253C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u253C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u253C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u253C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u253C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u253C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u253C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u253C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u253C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u253C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2524\\n\\u2502 0   \\u2502 hello-world-express \\u2502 default     \\u2502 1.0.0   \\u2502 fork    \\u2502 12345    \\u2502 42m    \\u2502 0    \\u2502 online    \\u2502 0%       \\u2502 45.2mb   \\u2502 ubuntu   \\u2502 disabled \\u2502\\n\\u2514\\u2500\\u2500\\u2500\\u2500\\u2500\\u2534\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2534\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2534\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2534\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2534\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2534\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2534\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2534\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2534\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2534\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2534\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2534\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2518\\n\"))), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Test Your Express Application\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Visit your Express application in a browser:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"https://cuXXXX.camp.lnu.se/hello-world-express/\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"The application should be working normally.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Test Crash Recovery\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Try the crash test route to see PM2's auto-restart feature:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"https://cuXXXX.camp.lnu.se/hello-world-express/test/crash\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"What happens:\")), mdx(\"ul\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"The application crashes (as intended)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"PM2 automatically detects the crash\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"PM2 immediately restarts the application\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Your application is available again within seconds\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Check PM2 status after the crash:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"pm2 status\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You should see the restart count has increased, showing that PM2 successfully\\nrestarted your application.\")), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Configure PM2 to Start on Server Boot\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"\\u26A0\\uFE0F Critical for Production:\"), \" Without this configuration, your applications\\nwill NOT restart automatically when the server reboots, leading to downtime.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"For a production server, it's essential that your applications start\\nautomatically when the server reboots. PM2 provides a simple way to configure\\nthis.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Generate the startup script:\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"pm2 startup\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This command will analyze your system and output a custom command that you need\\nto execute. The output will look similar to this:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"[PM2] Init System found: systemd\\n[PM2] To setup the Startup Script, copy/paste the following command:\\nsudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"\\uD83D\\uDCCB Important Steps:\")), mdx(\"ol\", {\n    parentName: \"li\"\n  }, mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Copy the command\"), \" that PM2 outputs in YOUR terminal (not the example above)\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Paste and execute\"), \" that specific command with sudo\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Each server generates a unique command\"), \" - yours will be different from\\nthe example\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Example of what to do:\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"# 1. Run pm2 startup first\\npm2 startup\\n\\n# 2. Copy and run the command that PM2 gives you, for example:\\nsudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"\\u26A0\\uFE0F Warning:\"), \" The exact command will be different on your system. Always use\\nthe command that PM2 generates for YOUR specific server configuration.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Save your current PM2 configuration:\")), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"pm2 save\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"This saves the current list of running applications so they're restored on boot.\"), mdx(\"p\", {\n    parentName: \"li\"\n  }, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Test the boot persistence:\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"You can test this by rebooting your server:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"sudo reboot\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"After the server comes back online, reconnect via SSH and check:\"), mdx(\"pre\", {\n    parentName: \"li\"\n  }, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"pm2 status\\n\")), mdx(\"p\", {\n    parentName: \"li\"\n  }, \"Your applications should be running automatically without any manual intervention.\"))), mdx(\"h3\", null, \"Monitor Your Applications\"), mdx(\"p\", null, \"View real-time logs from all applications:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"pm2 logs\\n\")), mdx(\"p\", null, \"View logs for a specific application:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"pm2 logs hello-world-express\\n\")), mdx(\"p\", null, \"Monitor processes in real-time:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-bash\"\n  }, \"pm2 monit\\n\")), mdx(\"p\", null, \"(Press \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"q\"), \" to exit the monitor)\"), mdx(\"h3\", null, \"\\u2705 What You've Accomplished\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Installed PM2 process manager\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Created a YAML ecosystem configuration file for managing multiple applications\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Configured centralized logging for your applications\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Set up automatic crash recovery and restart functionality\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Enabled applications to start automatically on server boot\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Learned essential PM2 monitoring and management commands\")), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"\\uD83C\\uDF10 Internet Traffic Flow:\\nClient \\u2192 nginx (HTTPS:443) \\u2192 Static Files (/var/www/hello-world)\\n                           \\u2192 Express App (PM2 \\u2192 localhost:3000)\\n\\n\\uD83D\\uDCC1 File System:\\n/var/www/\\n\\u251C\\u2500\\u2500 hello-world/                    # Static content\\n\\u2502   \\u2514\\u2500\\u2500 index.html\\n\\u251C\\u2500\\u2500 example-hello-world-express/    # Express application\\n\\u2502   \\u251C\\u2500\\u2500 package.json\\n\\u2502   \\u251C\\u2500\\u2500 src/server.js\\n\\u2502   \\u2514\\u2500\\u2500 node_modules/\\n\\u2514\\u2500\\u2500 ecosystem.config.yml           # PM2 process definitions\\n\\n\\uD83D\\uDD27 nginx Configuration:\\n/etc/nginx/\\n\\u251C\\u2500\\u2500 sites-enabled/cuXXXX.camp.lnu.se     # Main site config\\n\\u2514\\u2500\\u2500 conf.d/locations/\\n    \\u2514\\u2500\\u2500 hello-world-express.conf         # Express proxy config\\n\")), mdx(\"h3\", null, \"\\uD83D\\uDDDD\\uFE0F Key Benefits of Using PM2\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Process persistence\"), \" - Applications survive terminal disconnections\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Automatic restart\"), \" - Crashed applications are immediately restarted\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Centralized logging\"), \" - All application logs in one place\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Easy scaling\"), \" - Can run multiple instances of applications\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Boot persistence\"), \" - Applications start automatically when server boots\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Process monitoring\"), \" - Real-time monitoring of CPU, memory, and performance\")), mdx(\"p\", null, \"Your Express application is now running as a production-ready service that can\\nhandle crashes, server reboots, and provides comprehensive monitoring capabilities!\"), mdx(\"h2\", null, \"What's Next?\"), mdx(\"p\", null, \"Now that you have a fully functional web server with HTTPS, static content,\\ndynamic Express applications, and process management, you can:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Deploy more applications\"), \" - Add new location blocks for additional services\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Learn database integration\"), \" - Connect your Express apps to MongoDB or\\nPostgreSQL  \"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Implement monitoring\"), \" - Set up log analysis and performance monitoring\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Scale your applications\"), \" - Use PM2's cluster mode for better performance\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Add CI/CD\"), \" - Automate deployments from your GitLab repositories\")), mdx(\"p\", null, \"Your Cumulus virtual personal server is now a production-ready web server platform!\"), mdx(\"h2\", null, \"Summary: Your Server Stack\"), mdx(\"p\", null, \"You've successfully built a complete web server stack:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-plaintext\"\n  }, \"Internet \\u2192 nginx (HTTPS) \\u2192 Static Files (/var/www/hello-world)\\n                        \\u2192 Express App (PM2 managed, port 3000)\\n                        \\u2192 Future Applications (modular config)\\n\")), mdx(\"p\", null, mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Technologies mastered:\")), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\u2705 Linux system administration\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\u2705 SSH key management  \"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\u2705 nginx web server configuration\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\u2705 SSL/TLS certificate management\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\u2705 Node.js and Express development\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\u2705 Process management with PM2\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"\\u2705 Modular nginx configuration\")), mdx(\"h2\", null, \"Common Issues and Solutions\"), mdx(\"h3\", null, \"SSH Connection Problems\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"\\\"Permission denied\\\"\"), \": Check key permissions with \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ls -l ~/.ssh/id_cumulus_*\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"\\\"Host key verification failed\\\"\"), \": Run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"ssh-keygen -R cuXXXX.camp.lnu.se\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"\\\"Connection refused\\\"\"), \": Verify VPN connection and DNS name\")), mdx(\"h3\", null, \"nginx Issues\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"502 Bad Gateway\"), \": Check if your Node.js app is running with \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"pm2 status\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"403 Forbidden\"), \": Check file permissions in \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"/var/www/\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"404 Not Found\"), \": Verify file paths and nginx configuration syntax\")), mdx(\"h3\", null, \"PM2 Problems\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"App not starting\"), \": Check logs with \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"pm2 logs\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Port conflicts\"), \": Verify ports in ecosystem.config.yml\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Apps not restarting after reboot\"), \": Run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"pm2 startup\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"pm2 save\"))), mdx(\"h3\", null, \"SSL Certificate Issues\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Certificate expired\"), \": Run \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"sudo certbot renew\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Certificate errors\"), \": Verify with \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"sudo certbot certificates\")), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"strong\", {\n    parentName: \"li\"\n  }, \"Browser warnings\"), \": Clear cache or test in incognito mode\")));\n}\n;\nMDXContent.isMDXComponent = true;","tableOfContents":{"items":[{"url":"#what-is-cumulus","title":"What is Cumulus?"},{"url":"#step-1-verify-ssh-availability","title":"Step 1: Verify SSH Availability"},{"url":"#step-2-download-your-identity-file-from-gitlab","title":"Step 2: Download Your Identity File from GitLab"},{"url":"#step-3-create-the-ssh-directory","title":"Step 3: Create the .ssh Directory"},{"url":"#step-4-move-the-identity-file-to-ssh-directory","title":"Step 4: Move the Identity File to .ssh Directory"},{"url":"#step-5-set-correct-permissions-on-the-identity-file","title":"Step 5: Set Correct Permissions on the Identity File"},{"url":"#step-4-update-package-lists","title":"Step 4: Update Package Lists"},{"url":"#step-5-install-nodejs-and-npm","title":"Step 5: Install Node.js and npm"},{"url":"#step-6-install-and-start-nginx","title":"Step 6: Install and Start nginx"},{"url":"#step-7-verify-nginx-in-your-browser","title":"Step 7: Verify nginx in Your Browser"},{"url":"#step-8-request-and-install-a-tls-certificate-from-lnu-ftk-campus-ca","title":"Step 8: Request and Install a TLS Certificate from LNU FTK Campus CA"},{"url":"#step-9-configuring-automatic-certificate-renewal","title":"Step 9: Configuring Automatic Certificate Renewal","items":[{"url":"#how-automatic-renewal-works","title":"How Automatic Renewal Works"},{"url":"#1-configure-certbot-for-the-campus-ca-one-time-setup","title":"1. Configure Certbot for the Campus CA (One-time setup)","items":[{"url":"#1-create-a-drop-in-directory","title":"1. Create a drop-in directory"},{"url":"#2-create-the-drop-in-configuration-file","title":"2. Create the drop-in configuration file"}]},{"url":"#2-reload-systemd-and-test-renewal","title":"2. Reload systemd and test renewal"},{"url":"#3-verify-automatic-renewal-is-scheduled","title":"3. Verify automatic renewal is scheduled"},{"url":"#4-verify-renewal-results","title":"4. Verify renewal results"}]},{"url":"#step-10-trust-the-ca-in-your-browser","title":"Step 10: Trust the CA in Your Browser","items":[{"url":"#1-download-the-root-ca","title":"1. Download the Root CA"},{"url":"#2-import-the-certificate","title":"2. Import the Certificate","items":[{"url":"#google-chrome--safari-uses-macos-keychain","title":"Google Chrome / Safari (uses macOS Keychain)"},{"url":"#mozilla-firefox-separate-certificate-store","title":"Mozilla Firefox (separate certificate store)"},{"url":"#google-chrome","title":"Google Chrome"},{"url":"#mozilla-firefox","title":"Mozilla Firefox"},{"url":"#google-chrome-1","title":"Google Chrome"},{"url":"#mozilla-firefox-1","title":"Mozilla Firefox"}]},{"url":"#3-restart-your-browser","title":"3. Restart Your Browser"}]},{"url":"#step-11-verify-https-in-your-browser","title":"Step 11: Verify HTTPS in Your Browser"},{"url":"#step-12-secure-nginx-configuration","title":"Step 12: Secure nginx Configuration"},{"url":"#step-13-create-your-own-static-website","title":"Step 13: Create Your Own Static Website"},{"url":"#step-14-create-and-deploy-an-express-application","title":"Step 14: Create and Deploy an Express Application","items":[{"url":"#option-a-clone-from-gitlab-recommended","title":"Option A: Clone from GitLab (Recommended)"},{"url":"#option-b-create-from-scratch-fallback","title":"Option B: Create from Scratch (Fallback)"},{"url":"#configure-nginx-as-reverse-proxy","title":"Configure nginx as Reverse Proxy"}]},{"url":"#step-15-install-and-configure-pm2-process-manager","title":"Step 15: Install and Configure PM2 Process Manager","items":[{"url":"#monitor-your-applications","title":"Monitor Your Applications"},{"url":"#-what-youve-accomplished","title":"✅ What You've Accomplished"},{"url":"#️-key-benefits-of-using-pm2","title":"🗝️ Key Benefits of Using PM2"}]},{"url":"#whats-next","title":"What's Next?"},{"url":"#summary-your-server-stack","title":"Summary: Your Server Stack"},{"url":"#common-issues-and-solutions","title":"Common Issues and Solutions","items":[{"url":"#ssh-connection-problems","title":"SSH Connection Problems"},{"url":"#nginx-issues","title":"nginx Issues"},{"url":"#pm2-problems","title":"PM2 Problems"},{"url":"#ssl-certificate-issues","title":"SSL Certificate Issues"}]}]},"parent":{"relativePath":"veckoanvisningar/kursvecka-03/cumulus/your-first-cumulus-virtual-personal-server.md"},"frontmatter":{"metaTitle":"1DV026 | Your First Cumulus Virtual Personal Server","metaDescription":"metaDescription"}},"allMdx":{"edges":[{"node":{"fields":{"slug":"/","title":"Webbprogrammering på serversidan (1DV026)"}}},{"node":{"fields":{"slug":"/examinationsuppgifter","title":"Examinationsuppgifter"}}},{"node":{"fields":{"slug":"/ovningsuppgifter/02-ovningsuppgifter","title":"Övningsuppgifter"}}},{"node":{"fields":{"slug":"/ovningsuppgifter/delkurs-a","title":"Delkurs A"}}},{"node":{"fields":{"slug":"/ovningsuppgifter/delkurs-b","title":"Delkurs B"}}},{"node":{"fields":{"slug":"/samlingar","title":"Samlingar"}}},{"node":{"fields":{"slug":"/sandbox/basic-elements","title":"Basic elements"}}},{"node":{"fields":{"slug":"/sandbox/code","title":"Code"}}},{"node":{"fields":{"slug":"/sandbox/exercise","title":"Exercises"}}},{"node":{"fields":{"slug":"/sandbox/frontmatter","title":"Frontmatter"}}},{"node":{"fields":{"slug":"/sandbox/hint","title":"Hint"}}},{"node":{"fields":{"slug":"/sandbox","title":"Sandbox"}}},{"node":{"fields":{"slug":"/sandbox/markdown-import","title":"Imported markdown"}}},{"node":{"fields":{"slug":"/sandbox/mermaid","title":"Mermaid"}}},{"node":{"fields":{"slug":"/sandbox/tabs","title":"Tabs"}}},{"node":{"fields":{"slug":"/sandbox/youtube","title":"YouTube"}}},{"node":{"fields":{"slug":"/studieguide/examination","title":"Examination"}}},{"node":{"fields":{"slug":"/studieguide/forvantningar","title":"Förväntningar"}}},{"node":{"fields":{"slug":"/studieguide","title":"Studieguide"}}},{"node":{"fields":{"slug":"/studieguide/kommunikationsstrategi","title":"Kommunikationsstrategi"}}},{"node":{"fields":{"slug":"/studieguide/kursledning","title":"Kursledning"}}},{"node":{"fields":{"slug":"/studieguide/kursvardering","title":"Kursvärdering"}}},{"node":{"fields":{"slug":"/studieguide/larresurser","title":"Lärresurser"}}},{"node":{"fields":{"slug":"/studieguide/registrering","title":"Registrering"}}},{"node":{"fields":{"slug":"/studieguide/valkommen","title":"🎉 Välkommen!"}}},{"node":{"fields":{"slug":"/veckoanvisningar","title":"Veckoanvisningar"}}},{"node":{"fields":{"slug":"/examinationsuppgifter/delkurs-a/a1","title":"A1 - Online-test"}}},{"node":{"fields":{"slug":"/examinationsuppgifter/delkurs-a/a2","title":"A2 - Webbskrapan"}}},{"node":{"fields":{"slug":"/examinationsuppgifter/delkurs-a","title":"Delkurs A"}}},{"node":{"fields":{"slug":"/examinationsuppgifter/delkurs-b/b1","title":"B1 - CRUD"}}},{"node":{"fields":{"slug":"/examinationsuppgifter/delkurs-b/b2","title":"B2 - Realtidswebbapplikationen"}}},{"node":{"fields":{"slug":"/examinationsuppgifter/delkurs-b/b3","title":"B3 - Webbtjänsten"}}},{"node":{"fields":{"slug":"/examinationsuppgifter/delkurs-b","title":"Delkurs B"}}},{"node":{"fields":{"slug":"/examinationsuppgifter/om","title":"Om examination"}}},{"node":{"fields":{"slug":"/examinationsuppgifter/om/inlamning-via-mr","title":"Inlämning via Merge request"}}},{"node":{"fields":{"slug":"/samlingar/delkurs-a/inspelningar","title":"Stödjande inspelningar"}}},{"node":{"fields":{"slug":"/samlingar/delkurs-a/ovningar","title":"Övningar"}}},{"node":{"fields":{"slug":"/samlingar/delkurs-b","title":"Delkurs B"}}},{"node":{"fields":{"slug":"/samlingar/delkurs-b/inspelningar","title":"Stödjande inspelningar"}}},{"node":{"fields":{"slug":"/samlingar/delkurs-b/ovningar","title":"Övningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-01","title":"Kursvecka 1 - Nätverk"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-01/larmaterial","title":"Lärmaterial"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-01/oversikt","title":"Översikt"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-01/utmaningar","title":"Utmaningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-02","title":"Kursvecka 2 - Linux/Bash"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-02/larmaterial","title":"Lärmaterial"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-02/oversikt","title":"Översikt"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-02/ovningsuppgifter","title":"Övningsuppgifter"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-02/utmaningar","title":"Utmaningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03","title":"Kursvecka 3 - Webbservrar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/oversikt","title":"Översikt"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/utmaningar","title":"Utmaningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-04","title":"Kursvecka 4 - Plattformen Node.js"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-04/lasanvisningar","title":"Läsanvisningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-04/oversikt","title":"Översikt"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-04/ovningsuppgifter","title":"Övningsuppgifter"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-04/utmaningar","title":"Utmaningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-05","title":"Kursvecka 5 - Webbsäkerhet"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-05/lasanvisningar","title":"Läsanvisningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-05/oversikt","title":"Översikt"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-05/ovningsuppgifter","title":"Övningsuppgifter"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-05/utmaningar","title":"Utmaningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-06","title":"Kursvecka 6 - Applikationer och tjänster"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-06/oversikt","title":"Översikt"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-07-09","title":"Kursvecka 7-9 - Webbapplikationers arkitektur"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-07-09/oversikt","title":"Översikt"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-07-09/ovningsuppgifter","title":"Övningsuppgifter"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-10-11","title":"Kursvecka 10-11 - Persistent data"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-10-11/lasanvisningar","title":"Läsanvisningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-10-11/oversikt","title":"Översikt"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-10-11/ovningsuppgifter","title":"Övningsuppgifter"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-10-11/utmaningar","title":"Utmaningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-12-13","title":"Kursvecka 12-13 - Access control"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-12-13/lasanvisningar","title":"Läsanvisningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-12-13/oversikt","title":"Översikt"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-12-13/utmaningar","title":"Utmaningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-14-15","title":"Kursvecka 14-15 - Realtidsapplikationer"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-14-15/oversikt","title":"Översikt"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-14-15/utmaningar","title":"Utmaningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-16-19","title":"Kursvecka 16-19 - Restful Services"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-16-19/lasanvisningar","title":"Läsanvisningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-16-19/oversikt","title":"Översikt"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-16-19/utmaningar","title":"Utmaningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-20","title":"Kursvecka 20 - Examination"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-20/oversikt","title":"Översikt"}}},{"node":{"fields":{"slug":"/examinationsuppgifter/om/resurser/kom-igang","title":"Kom Igang"}}},{"node":{"fields":{"slug":"/examinationsuppgifter/om/resurser/mr","title":"Mr"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-01/resurser/inspelningar","title":"Inspelningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-02/resurser/inspelningar","title":"Inspelningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-02/resurser/ovningar","title":"Ovningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/_old/ovningsuppgifter","title":"Övningsuppgifter"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/cscloud/00-prerequisites","title":"Prerequisites"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/cscloud/01-connect","title":"1 - Connect to CSCloud"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/cscloud/02-update","title":"2 - Update and install"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/cscloud/03-NGINX","title":"3 - NGINX"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/cscloud/05-http2","title":"5 - HTTP2"}}},{"node":{"fields":{"slug":"/examinationsuppgifter/om/kom-igang","title":"Kom igång med en uppgift"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/cscloud/08-run","title":"8 - Executing"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/cscloud/04-https","title":"4 - TLS (https)"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/cscloud/06-PM2","title":"6 - Process Manager"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/cscloud/09-troubleshoot","title":"10 - Troubleshoot"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/cscloud","title":"Guide CSCloud"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/cumulus","title":"Guide Cumulus"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/resurser/inspelningar","title":"Inspelningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/cumulus/your-first-cumulus-virtual-personal-server","title":"Your First Cumulus Virtual Personal Server"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/resurser/ovningar","title":"Ovningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-04/resurser/inspelningar","title":"Inspelningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-05/resurser/inspelningar","title":"Inspelningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-04/resurser/ovningar","title":"Ovningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-05/resurser/ovningar","title":"Ovningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-06/resurser/inspelningar","title":"Inspelningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-07-09/resurser/ovningar","title":"Ovningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-07-09/resurser/inspelningar","title":"Inspelningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-10-11/resurser/inspelningar","title":"Inspelningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-10-11/resurser/ovningar","title":"Ovningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-12-13/resurser/inspelningar","title":"Inspelningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-14-15/resurser/inspelningar","title":"Inspelningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-16-19/resurser/inspelningar","title":"Inspelningar"}}},{"node":{"fields":{"slug":"/veckoanvisningar/kursvecka-03/cscloud/07-files","title":"7 - Files"}}},{"node":{"fields":{"slug":"/samlingar/delkurs-a","title":"Delkurs A"}}},{"node":{"fields":{"slug":"/examinationsuppgifter/om/muntlig-examination","title":"Muntlig examination"}}}]}},"pageContext":{"id":"5c9f0212-55a6-57b9-97f6-eebb75ca10f6"}},
    "staticQueryHashes": ["253607798","3706406642","710574383"]}