Skip to content

1. Your personal website

What you'll have at the end: a page at https://your-username.github.io with your name, what you work on, your education, your projects, and links to your GitHub and LinkedIn. A real URL you can put on a CV.

How long: about two hours, taking your time. Your page will be live on the internet after the first twenty minutes — everything after that is making it better.

What you need to know already: nothing. If you've written a Python script before, some of this will feel familiar, but it isn't assumed.


1. The goal

What you're building is a plain, fast, readable page: your name, a line about what you work on, and links to the rest of you. No framework, no template, no cookie banner. One HTML file and one CSS file that you wrote and understand completely.

That last part matters more than it sounds. A page you built by hand is a page you can change at 11pm the night before an application deadline.

Screenshot of katarinagresova.com: a name in large type, the line "Machine
learning for biology, built to be reused", three short paragraphs of bio, a photo, and
a row of links to email, GitHub, Google Scholar, ORCID and LinkedIn. Screenshot of katarinagresova.com: a name in large type, the line "Machine
learning for biology, built to be reused", three short paragraphs of bio, a photo, and
a row of links to email, GitHub, Google Scholar, ORCID and LinkedIn.

My own page at katarinagresova.com — the same GitHub Pages setup you'll have running in twenty minutes, with a lot of small changes layered on since. It started life at katarinagresova.github.io, exactly where yours will start; the custom domain came years later and is an afternoon's work whenever you want it (§8). Yours will start plainer than this, and that's the point: plain and live beats elaborate and unpublished.

About the hand-written HTML

This site you're reading is not hand-written HTML — it's generated by a tool called MkDocs. So why teach you the hard way?

Because generators hide the raw thing, and the raw thing is what you need to understand. Once you've written HTML by hand, a generator is a convenience. If you start with the generator, it's magic — and when it breaks, you're stuck. Switching to one later is easy; starting there and working backwards is not.


2. Accounts and tools

Three things. Do all three now; it's the boring part and it's over quickly.

A GitHub account

Sign up at github.com if you don't have one.

Your username matters here — it becomes part of your website address, and it's the name other people see on your code. Pick something you'd be happy putting on a CV. Some version of your actual name is the safe choice. bunnyrabbit95 is a choice you will regret at a job interview.

If you already have an account with a username you hate, you can change it in Settings → Account. Do that now rather than later; it gets more disruptive the more you've built.

Git

Git is the program that tracks changes to your files and sends them to GitHub.

Open Terminal ( Spotlight → type "Terminal") and run:

git --version

If you get a version number, you're done. If macOS offers to install "command line developer tools", say yes and wait.

Install Git for Windows. Accept all the default options in the installer.

This gives you a program called Git Bash. Use Git Bash for every command in this tutorial — not PowerShell and not Command Prompt. Git Bash gives you the same commands as macOS and Linux, which means the rest of this tutorial applies to you unchanged.

sudo apt install git    # Debian, Ubuntu
sudo dnf install git    # Fedora, RHEL

Then tell git who you are. It stamps this onto every change you make:

git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"

Use the same email address as your GitHub account, so GitHub knows the changes are yours.

A text editor

VS Code is the standard choice and it's free. Install it.

Do not write HTML in Microsoft Word. Word will silently turn your quote characters into curly quotes and your page will break in a way that's genuinely hard to diagnose.


3. Get something live

Now the part that matters. We're going to put a page on the internet before we learn anything at all about how it works.

Create the magic repository

A repository (or "repo") is a folder that git tracks. On GitHub, it's a project.

GitHub has one special rule: if you create a repository named exactly your-username.github.io, GitHub publishes its contents as a website at that address.

  1. Go to github.com/new.
  2. In Repository name, type your-username.github.io — with your username substituted in, exactly as it's spelled on your account. If your username is agnesbio, the repository is agnesbio.github.io.
  3. Set it to Public. Private repositories don't get published on the free plan.
  4. Tick Add a README file.
  5. Click Create repository.

The name has to be exact

agnesbio.github.io works. AgnesBio.github.io works too (GitHub is relaxed about capitals here). agnesbio.github.com does not. agnes.github.io does not, unless your username is agnes.

If the address doesn't work later, this is the first thing to check.

Add a page

We'll do this in the browser for now — no terminal yet.

  1. In your new repository, click Add fileCreate new file.
  2. Name the file index.html. The name matters: index.html is what a web server shows when someone visits the address with nothing after the slash.
  3. Paste this in:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Your Name</title>
</head>
<body>
  <h1>Hello, I am on the internet</h1>
</body>
</html>
  1. Scroll down and click Commit changes, then Commit changes again in the dialog.

Turn on Pages

  1. In the repository, click Settings (top bar, on the right).
  2. In the left sidebar, click Pages.
  3. Under Source, choose Deploy from a branch.
  4. Branch: main, folder: / (root). Click Save.

Wait, then look

Go to https://your-username.github.io in a new tab.

The first time, it takes one to three minutes. You may get a 404 in the meantime — that's normal, it means GitHub hasn't finished publishing yet. Refresh every thirty seconds.

You can watch the progress in the Actions tab of your repository: an orange dot means it's building, a green tick means it's done.


Stop here for a second

That's a real website, at a real address, served to anyone in the world who types it in. You can send that link to someone right now and they'll see it.

Everything from here on is improving a thing that already exists, which is a much better position to be in than building toward something that doesn't.


4. The git loop

You've now made one change through the GitHub website. That works, but it's slow and you can't preview anything before it goes public. So we move to working on your own computer — and for that you need git.

Here's the whole mental model:

Git keeps a list of saved versions of your project. You choose what goes into each saved version, you write a note about what changed, and then you send your saved versions up to GitHub.

Four commands do almost all of it:

Command What it does Plain English
git status Shows what's changed "Where am I, what have I done?"
git add <file> Marks a file to be included "Include this in the next save"
git commit -m "message" Saves a version "Save it, with this note"
git push Sends saves to GitHub "Send it up"

That's it for now. Git has branches, a staging area with its own theory, rebasing, and a reputation for being confusing. You don't need any of it today. One person working on one website needs those four commands.

git status is the one to lean on. Run it constantly — before and after everything. It's the only command here that can't break anything, and it usually tells you what to do next.


5. Working on your own computer

Connecting your computer to GitHub

Your computer needs to prove it's allowed to push to your repositories. You do that with an SSH key.

The idea is simpler than the name makes it sound. You generate a pair of files. One is private and never leaves your computer. The other is public, and you paste it into GitHub. From then on GitHub recognises this computer and lets it push — no password, ever again.

This is worth doing properly, because it isn't really a GitHub thing. It's the same kind of key, made by the same command, that gets you into a compute cluster or a remote server later.

1. Generate the key

ssh-keygen -t ed25519 -C "your.email@example.com"

Use the same email address as your GitHub account. It asks you three questions:

  • Enter file in which to save the key → press Enter to accept the default, ~/.ssh/id_ed25519.
  • Enter passphrase → press Enter for none, or type one. A passphrase protects the key if your laptop is stolen, at the cost of typing it every time you push. On a computer only you use, empty is a reasonable choice.
  • Enter same passphrase again → the same again.

You now have two files in ~/.ssh/:

File What it is
id_ed25519 Private. Stays on this computer. Never send it anywhere.
id_ed25519.pub Public. This is the one you give to GitHub.

Only the .pub file leaves your computer

The two names differ by four characters, and that difference is the entire security model. Anyone holding the private key can act as you on GitHub. If you ever paste it somewhere by accident, generate a new pair and delete the old key from GitHub — it takes a minute, and it's much better than the alternative.

2. Copy the public key

cat ~/.ssh/id_ed25519.pub

You get one long line starting with ssh-ed25519 and ending with your email address. Select the whole thing and copy it — from ssh- to the last character, with no line breaks in the middle.

3. Give it to GitHub

  1. Go to github.com/settings/keys.
  2. Click New SSH key.
  3. Title: something that tells you which machine it is — laptop, work desktop. You'll accumulate a few of these over the years, and "key 1" will mean nothing to you.
  4. Key type: Authentication key.
  5. Paste into the Key box and click Add SSH key.

4. Test that it works

Don't skip this. It's one command, and a problem is far easier to understand now than in the middle of your first push:

ssh -T git@github.com

The first time, it tells you it has never seen github.com before:

The authenticity of host 'github.com (140.82.121.4)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
Are you sure you want to continue connecting (yes/no/[fingerprint])?

Type yes and press Enter. That fingerprint is GitHub's own, and GitHub publishes it — it should match the line above character for character. Then you should see:

Hi your-username! You've successfully authenticated, but GitHub does not provide shell access.

That is success, despite the "but". It's telling you two things: your key works, and github.com isn't a machine you can log into and use as a terminal. Your own username in that greeting is what you're checking for.

Your GitHub password won't work

If git ever asks for a username and password in the terminal, your account password is not the answer — GitHub stopped accepting it in 2021. It means git is talking to GitHub over HTTPS instead of using your key. See when things go wrong.

If you'd rather use the GitHub CLI

The GitHub CLI can set all this up for you: install gh, run gh auth login, and choose "Login with a web browser". On your own laptop that is a perfectly good choice.

The reason this tutorial teaches keys instead is that gh is another program to install, and it usually isn't available — and can't be installed — on the shared compute clusters where you're most likely to want this. ssh-keygen is already on every machine that has git.

Where am I?

Before you download anything, a word about the single biggest source of beginner confusion: the terminal is always sitting in some folder, and commands act on that folder. If a command doesn't do what you expect, the reason is very often that you're somewhere else than you think.

Three commands:

pwd     # "print working directory" — which folder am I in?
ls      # "list" — what's in it?
cd      # "change directory" — go somewhere else

Try them:

pwd
ls
cd Desktop
pwd
ls

Some useful moves for cd:

cd ..              # up one level, to the containing folder
cd ~               # to your home folder
cd                 # also to your home folder
cd -               # back to where you just were

Press Tab

Type the first few letters of a folder name and press Tab. The shell completes it. This is not a nicety — it prevents typos, and typos in paths are the cause of a large share of "but it worked yesterday" problems.

Get a copy of your repository

Navigate to wherever you want to keep it. Somewhere you'll find again — not Downloads:

cd ~
mkdir -p projects
cd projects

Then clone it — make a local copy that stays connected to GitHub:

git clone git@github.com:your-username/your-username.github.io.git
cd your-username.github.io
ls

You should see index.html and README.md. This folder is now a git repository: git is watching it, and it knows where on GitHub it came from.

Copy the SSH address, not the HTTPS one

If you get that address from the green Code button on GitHub rather than typing it out, notice that the button opens on the HTTPS tab. Click SSH first. The two look alike, and only the SSH one uses the key you just set up:

git@github.com:agnesbio/agnesbio.github.io.git        ← SSH, what you want
https://github.com/agnesbio/agnesbio.github.io.git    ← HTTPS, will ask for a password

The : after github.com is not a typo. SSH addresses use a colon there, not a slash.

Edit, preview, push

Open the folder in VS Code (File → Open Folder, or run code . from the terminal, the . meaning "this folder").

Change the heading in index.html to something else, and save the file. VS Code does not auto-save by default; a white dot on the tab means unsaved.

Now preview it. You could double-click the file, but get into the habit of running a small local web server instead — it behaves the way the real site does:

python -m http.server 8000

Open http://localhost:8000 in your browser. That's your page, served from your own computer. Nobody else can see it.

Leave that running — it's a program that keeps going until you stop it. Open a second terminal window for git commands. When you want to stop the server, click its window and press Ctrl+C.

python vs python3

If python isn't found, try python3 -m http.server 8000. On some systems only one of them exists.

Now the loop, in your second terminal:

git status

It shows index.html in red, under "Changes not staged for commit". Git noticed.

git add index.html
git commit -m "Change the heading"
git push

Refresh https://your-username.github.io after a minute. Your change is live.

That's the loop. Edit, save, look at localhost, add, commit, push. You'll do it a few dozen times today and then it'll be automatic.

Commit messages

Write what changed, in a few words: "Add education section", "Fix broken LinkedIn link". Not "update", not "asdf", not "changes". Future you will read these while trying to work out when something broke.


6. Build the actual page

Now make it yours. Here's a full skeleton — replace everything in [square brackets] and delete what doesn't apply:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>[Your Name]</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>

  <header>
    <h1>[Your Name]</h1>
    <p class="tagline">[MSc student in molecular biology, moving into computational work.]</p>
  </header>

  <main>

    <section>
      <h2>About</h2>
      <p>
        [Two or three sentences. What you work on, what you're interested in, what you're
        looking for. Write it as if explaining to a person, not to a form.]
      </p>
    </section>

    <section>
      <h2>Education</h2>
      <ul>
        <li><strong>[MSc Molecular Biology]</strong>, [University] — [2023&ndash;2025]</li>
        <li><strong>[BSc Biology]</strong>, [University] — [2020&ndash;2023]</li>
      </ul>
    </section>

    <section>
      <h2>Projects</h2>
      <ul>
        <li>
          <strong>[Project name]</strong>          [One sentence on what it does and what you used.]
          <a href="[link to the GitHub repository]">Code</a>
        </li>
      </ul>
    </section>

    <section>
      <h2>Contact</h2>
      <ul>
        <li><a href="mailto:[you@example.com]">[you@example.com]</a></li>
        <li><a href="https://github.com/[your-username]">GitHub</a></li>
        <li><a href="https://www.linkedin.com/in/[your-profile]/">LinkedIn</a></li>
      </ul>
    </section>

  </main>

</body>
</html>

The HTML you actually need

You've now met most of it:

  • <h1>, <h2> — headings. One <h1> per page, your name.
  • <p> — a paragraph.
  • <ul> and <li> — an unordered list and its items.
  • <a href="...">text</a> — a link. href is where it goes, the text is what's shown.
  • <strong> — important, rendered bold.
  • <section>, <header>, <main> — invisible boxes that group things. They don't change how the page looks; they say what each part is, which helps screen readers and search engines, and gives you something to point CSS at.

Tags come in pairs: <p> opens, </p> closes. The commonest beginner bug is a missing closing tag, and the symptom is that the rest of the page looks wrong rather than the bit you were editing.

Make it look decent

Create a second file called style.css in the same folder. The <link> tag already in your HTML pulls it in.

body {
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  line-height: 1.6;
  max-width: 42rem;
  margin: 3rem auto;
  padding: 0 1.25rem;
  color: #24292f;
}

h1 {
  margin-bottom: 0.25rem;
}

.tagline {
  color: #57606a;
  margin-top: 0;
}

a {
  color: #0969da;
}

section {
  margin-top: 2.5rem;
}

That's deliberately short. Four ideas do nearly all the work:

  • max-width — the single highest-value line here. Text running the full width of a monitor is genuinely hard to read; the eye loses its place on the return sweep. Around 40–45 characters-wide-ish (42rem) is comfortable.
  • margin: 3rem auto — space above and below, and auto left/right, which centres the block. This is the standard trick for centring a page.
  • line-height: 1.6 — space between lines. Browsers default to something tight.
  • color — pure black on pure white is harsher than it needs to be. #24292f is a very dark grey and reads more comfortably.

Resist adding more for now. A plain page that's easy to read beats an elaborate one, and the failure mode of a first personal site is always over-decoration, never under.

Save both files, refresh localhost:8000, then commit:

git add index.html style.css
git commit -m "Add content and stylesheet"
git push

Adding everything at once

Once you're comfortable, git add . stages every changed file in the current folder, which saves typing. Run git status first so you know what . is about to include.


7. When things go wrong

This section is the one to come back to. Expand whichever matches.

My page shows 404

In rough order of likelihood:

  1. It's still building. The first deploy takes a couple of minutes. Check the Actions tab of your repository — orange dot means in progress.
  2. The repository name is wrong. It must be exactly your-username.github.io, with your real username. Check the spelling against your profile page.
  3. The repository is private. Settings → scroll to the bottom → change visibility to public.
  4. There's no index.html. Or it's named Index.html, or index.htm, or it's inside a subfolder instead of at the top level. It must be index.html at the root.
  5. Pages isn't enabled. Settings → Pages → Source: Deploy from a branch, branch main, folder / (root).
I pushed, but the site hasn't changed
  1. Give it a minute. Deployment isn't instant. The Actions tab tells you when it's done.
  2. Your browser cached the old version. Hard-refresh: Ctrl+Shift+R (Windows, Linux) or Cmd+Shift+R (macOS). Or open the page in a private window, which is the reliable test.
  3. The push didn't actually happen. Run git status. If it says "Your branch is ahead of 'origin/main' by 1 commit", the commit exists locally but wasn't pushed — run git push.
  4. You didn't save the file. Check for a white dot on the VS Code tab.
fatal: not a git repository

You're running a git command in a folder that git doesn't track. Almost always: you're one level too high, or one level too low.

pwd     # where am I?
ls -a   # is there a .git folder here?

If there's no .git, cd into your project folder and try again. cd .. goes up one level.

Updates were rejected because the remote contains work that you do not have locally

Someone — usually you, through the GitHub website — changed the repository since you last downloaded it. Your local copy is missing that change, and git won't overwrite it.

git pull
git push

git pull fetches the changes and merges them into your copy. If it opens a text editor asking for a merge message, that's normal: press Ctrl+X if it looks like nano, or Esc then type :wq and Enter if it looks like vim.

To avoid this: pick one place to edit — your computer — and stop editing through the website.

git push asks for a username and password

Your account password won't work; GitHub disabled that in 2021. What's happening is that this repository is talking to GitHub over HTTPS instead of using your SSH key. Check which one it's using:

git remote -v

If the address starts with https://github.com/, point it at SSH instead:

git remote set-url origin git@github.com:your-username/your-username.github.io.git

Run git remote -v again to confirm it changed, then push. If it still asks, the key itself is the problem — see the next box.

git@github.com: Permission denied (publickey)

GitHub isn't recognising your key. Test it on its own:

ssh -T git@github.com

If that fails too, it's one of three things:

  1. There's no key on this computer. ls ~/.ssh/ should list id_ed25519 and id_ed25519.pub. If it doesn't, generate one — step 5.
  2. The key never reached GitHub. Open github.com/settings/keys and compare what's listed there against the output of cat ~/.ssh/id_ed25519.pub.
  3. You're on a different computer than the one you set up. Keys are per-machine, and that's deliberate. Generate a key here too and add it — one account can hold many, which is how you revoke a lost laptop without disturbing anything else.
It asks for my key passphrase every single time

That is what a passphrase does. To type it once per session instead of once per push, hand the key to the ssh-agent:

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

On macOS, ssh-add --apple-use-keychain ~/.ssh/id_ed25519 stores it in the keychain and you won't be asked again on that machine.

nothing to commit, working tree clean — but I changed something

Three possibilities:

  1. You didn't save the file in your editor.
  2. You saved it somewhere else — a different copy of index.html in another folder. Run pwd and check you're in the folder you think you are.
  3. You already committed it. Run git log --oneline to see your recent commits.
My CSS isn't doing anything
  1. File name mismatch. The <link> tag says href="style.css", so the file must be called exactly style.css, in the same folder as index.html. styles.css won't work.
  2. Cached. Hard-refresh (Ctrl+Shift+R / Cmd+Shift+R).
  3. A missing } or ;. CSS fails quietly: one syntax error can silently kill every rule after it. Check the block just above the first rule that stopped working.
The page looks broken after I edited the HTML

Look for an unclosed tag. Every <p> needs a </p>, every <section> a </section>.

In VS Code, click just after an opening tag — it highlights the matching closing tag. If nothing highlights, you've found your problem.

command not found: python

Try python3 -m http.server 8000. If neither exists, install Python from python.org, or just open index.html by double-clicking it for now — the local server is a convenience, not a requirement.

The terminal is stuck and won't take new commands

Something is still running — probably python -m http.server, which runs until you stop it. Press Ctrl+C to stop it.

If you're in a text editor you didn't mean to open: Ctrl+X exits nano, and Esc then :q! then Enter exits vim.


8. Going further

Optional, in increasing order of effort:

  • A favicon. The little icon in the browser tab. Drop a favicon.ico or favicon.png at the root and add <link rel="icon" href="favicon.png"> to your <head>.
  • A photo. <img src="me.jpg" alt="Photo of [your name]" width="160">. Always write the alt text — it's what screen readers announce and what shows if the image fails.
  • A PDF of your CV. Put cv.pdf in the folder, link to it. Now you have a stable URL you can give people that always has the current version.
  • More pages. projects.html next to index.html is reachable at your-username.github.io/projects.html. Link between them.
  • A custom domain. Buy yourname.com and put it in Settings → Pages → Custom domain. At the registrar you point four A records at GitHub's addresses and a www CNAME at your-username.github.io; GitHub's own page lists the addresses and issues the HTTPS certificate for you. Around €10–15 a year, and it makes a noticeable difference to how a link reads. Nothing breaks when you do it: the old address redirects to the new one, and every project page follows, so your-username.github.io/thesis-code/ starts landing on yourname.com/thesis-code/.
  • A static site generator. When you have more than four or five pages, copying the same header into each one gets old. That's the moment to switch — and the moment it'll make sense, which is exactly why we didn't start there.

What you actually learned

Worth naming, because it's more than it felt like:

  • Git: status, add, commit, push, pull, clone — the loop that essentially all software work runs on.
  • The shell: pwd, ls, cd, running a program, stopping it with Ctrl+C, and the idea of a working directory.
  • HTML and CSS: enough to build and change a page without copying a template.
  • Deployment: you have a thing on the internet, and you know how it got there.

And a public URL with your name on it, which is the point.


Where to go next

Two tutorials carry on from here, and you can do them in either order.

The file too big for Excel takes the terminal you've just met and points it at 1.2 million rows of real human genetics data — a file a spreadsheet physically cannot open — and ends with a second repository on your account.

Redesign your website without fear comes back to this site. Branches, so you can try an idea without committing to it, and five ways to undo, so that "I've broken it" stops being a reason not to touch the page. If you already want to change something here and are nervous about it, go there first.

Where did you get stuck?

If any step here was confusing, wrong, or missing something — open an issue. The "when things go wrong" section above is built entirely out of those reports, and it's the most useful part of this page.