Change saved gutenberg blocks

When a post is saved the Gutenberg Blocks are rendered and it’s html is saved in the post_content in the database, most of the time. But what if you want to change the rendered html for all posts in every post.
Here we will explain how to change the html of saved Gutenberg blocks.

Add a class to all paragraph blocks

This use case is highly unlikely, but it is a simple one. So making it easier to explain.
So lets assume you would want to add a newer class to every paragraph on a single post on your site.

To start the block looks like this:

<!-- wp:paragraph -->
<p>To start the block looks like this:</p>
<!-- /wp:paragraph -->

With the added newer class it will look like this:

<!-- wp:paragraph {"className":"newer"} -->
<p class="newer">To start the block looks like this:</p>
<!-- /wp:paragraph -->

With the use case explained lets show the code to handle this.

changing the html of a block

The following code will change this html

<?php
$post                 = get_post( 898 ); // Get 1 post
$parsed_blocks        = parse_blocks( $post->post_content ); // parse all blocks in this post.
$updated_post_content = ''; // The updated post content will be inside here.

// Loop over the parsed blocks.
foreach ( $parsed_blocks as $block ) {
	if ( 'core/paragraph' !== $block['blockName'] ) {
		// only convert paragraphs, the rest we re-add untouched.
		$updated_post_content .= serialize_block( $block );
		continue;
	}

	// Load the innerHTML as an object so it's safer to manipulate.
	$dom = new DOMDocument;
	$dom->loadHTML( $block['innerHTML'], LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );

	/** @var DOMElement $paragraph_node Get the <p> as a object */
	$paragraph_node = $dom->getElementsByTagName( 'p' )->item( 0 );
	// Get already set classes.
	$current_classes = $paragraph_node->getAttribute( 'class' );
	// Append `newer` class to the current list.
	$all_classes = trim( $current_classes . ' newer' );

	// Add the class attribute.
	$paragraph_node->setAttribute( 'class', $all_classes );
	$new_content = $dom->saveHTML();

	// Now put the new block together.
	$new_block = array(
		// We keep this the same.
		'blockName'    => 'core/paragraph',
		// also add the class as block attributes.
		'attrs'        => array( 'className' => $current_classes ),
		// I'm guessing this will come into play with group/columns, not sure.
		'innerBlocks'  => array(),
		// The actual content.
		'innerHTML'    => $new_content,
		// Like innerBlocks, I guess this will is used for groups/columns.
		'innerContent' => array( $new_content ),
	);

	$updated_post_content .= serialize_block( $new_block );
}

// Update the post with the content.
wp_update_post( array( 'ID' => $post->ID, 'post_content' => $updated_post_content, ) );

Run this code once and it will update this one post, ID 898 in this case. Pretty much every line has an explaining comment. But let’s go through the main steps.

First we get the post we want and convert to content to an array of blocks.
We loop over these blocks one by one.
As we only are changing the paragraphs we check and re-add other block types.

The paragraph blocks we load as a DomDocument. We only need to add or edit one attribute. you might be able to do this with a str_replace but DomDocument is more flexible and it can handle edge cases with more safety.

After that we check for existing classes. And add our newer class to the existing ones.
Even if there are no existing ones that’s fine.
Once the class is added we re-render the html content of the block.

The last thing we do is re-create an array that is the definition of a block. And render is as a html block.

Finally after all blocks are looped over we update the post.
You now updated all the Paragraph Gutenberg blocks in the post.

Change multiple posts

Now we covered updating a single post, we can pretty much wrap this in a loop to cover all posts.

<?php
$all_post_types = get_posts( array(
	'post_type'   => array( 'post', 'page' ), // add any posttype you would want to check.
	'post_status' => 'any', // all except trash.
	'nopaging'    => true, // no limits, get all posts.
) );

foreach ( $all_post_types as $post ) {
	// insert the code from the sample above
}

Closing thoughts

Keep in mind the final code you only need to run it once.
Before doing this I recommend creating a test post with a bunch of the same blocks but with all kind of options/settings tweaked. That should give you a good test case.

And of course create a backup before converting all posts and do deep check when it’s done.

Add Color Pallets to Gutenberg child theme

How to customize the color pallets you can select in a gutenberg block in your theme. And the tweak if you are running a childtheme.

In Gutenberg you can customize the colors of the some blocks like the paragraph block (this is a paragraph block.) The theme can add some default colors to pick from or a user can pick his a custom color.

Define theme color pallets

In a theme add the following code to the functions.php:

add_theme_support( 'editor-color-palette', array(
	array(
		'name'  => __( 'grey', 'theme translation' ),
		'slug'  => 'grey',
		'color' => '#111111',
	),
	array(
		'name'  => __( 'red', 'theme translation' ),
		'slug'  => 'red',
		'color' => '#ee4035',
	),
	array(
		'name'  => __( 'orange', 'theme translation' ),
		'slug'  => 'orange',
		'color' => '#f37736',
	),
	array(
		'name'  => __( 'yellow', 'theme translation' ),
		'slug'  => 'yellow',
		'color' => '#fdf498',
	),
	array(
		'name'  => __( 'green', 'theme translation' ),
		'slug'  => 'green',
		'color' => '#7bc043',
	),
	array(
		'name'  => __( 'blue', 'theme translation' ),
		'slug'  => 'blue',
		'color' => '#0392cf',
	),
) );

Above is the example of the 6 colors that this theme uses. The name is used when hovering over the color pallet. the slug shouldn’t matter as long as they are different.
Finally the color is simple hexadecimal color notation.

This will work on normal theme’s. But not on child themes.

Define color pallets for child themes.

The following might differ a bit depending on the parent theme setup. But the trick is to add the colors after the parent does it. My theme is a child of twentysixteen.

The best way to overrule the parent should be to add the code as this:

function janwsixteen_after_setup() {
	// Adds support for editor color palette.
	add_theme_support( 'editor-color-palette', array(
		array(
			'name'  => __( 'grey', 'theme translation' ),
			'slug'  => 'grey',
			'color' => '#111111',
		),
		array(
			'name'  => __( 'red', 'theme translation' ),
			'slug'  => 'red',
			'color' => '#ee4035',
		),
		array(
			'name'  => __( 'orange', 'theme translation' ),
			'slug'  => 'orange',
			'color' => '#f37736',
		),
		array(
			'name'  => __( 'yellow', 'theme translation' ),
			'slug'  => 'yellow',
			'color' => '#fdf498',
		),
		array(
			'name'  => __( 'green', 'theme translation' ),
			'slug'  => 'green',
			'color' => '#7bc043',
		),
		array(
			'name'  => __( 'blue', 'theme translation' ),
			'slug'  => 'blue',
			'color' => '#0392cf',
		),
	) );
}
add_action( 'after_setup_theme', 'janwsixteen_after_setup', 100 );

Now this code will run during the after_setup_theme event at priority 100 (which is a late priority).
If this does not work I recommend setting replacing the action after_setup_theme to init.

Check DNS records in terminal

Currently I’m working on a big server migration. Where a lot of small sites get moved over. And a lot of the domain names are registered and managed at third parties.
So after each move I have to check a lot of DNS records to see if the move is final.

Usually I use the mxtoolbox which is great to check DNS records.
But I needed a more bulk option for this mass migration.

Enter the dig command

Dig commands

Check A record:

dig example.com A +short

Check AAAA record (ipv6)

dig example.com AAAA +short

Check CNAME

dig example.com CNAME +short

Check A, AAAA and CNAME all at once.

dig  example.com AAAA example.com CNAME example.com A +short

These commands return very compact ip’s or domainnames.
Which are easy to scan.

I’m not sure if these are cached.

WordPress Settings API save post

The WordPress Settings API (docs) is used to create settings pages and save settings in the options table. But I didn’t need a setting, I needed a settings page to create/update a post.

Of course I could have created my own custom settings page and not use the settings API. But the settings API does have a couple of advantages.

  • Nonces validation.
  • Still using a WordPress standard.
  • Saving and error notices by default and customizable.
  • Mix with regular options.

First off create a settings page like we are used to:

<?php

add_action( 'admin_menu', 'register_menu_item' );

function register_menu_item() {
	add_submenu_page(
		'options-general.php',
		__( 'Settings page title' ),
		__( 'Menu title' ),
		'manage_options',
		'page-slug',
		function () {
			require './templates/settings-page.php';
		}
	);
}

add_action( 'admin_init', 'register_settings' );

/**
 * Register settings to save.
 */
function register_settings() {
	register_setting(
		'fake-option',
		'fake-option',
		array( 'sanitize_callback' => 'save_post' )
	);
}

/**
 * This function will only be called when the "option" is valid to save.
 * Here we validate our own fields and create the post we want.
 * 
 * @param null|array $unused_fake_setting
 */
function save_post( $unused_fake_setting ) {
	// Nonces are validated before we get here.

	static $first_run = true; // this function can be called twice, but we only need it once.
	if ( ! $first_run ) {
		return;
	}
	$first_run = false; // Never run again, this request.

	if ( empty( $_POST['log_title'] ) ) {
		add_settings_error( 'fake-option', 'title-empty', __( "Log title can't be empty." ) );

		return;
	}
	$title = wp_unslash( $_POST['log_title'] );
	$title = sanitize_text_field( $title );

	// validate other fields...
    
    
    // save the post.
	wp_insert_post( array(
		'post_title' => $title
		// add other attributes.
	) );

	// success message, yes the function name is confusing.
	add_settings_error( 'wpjm-ch-save', 'term-added', __( 'Worker successfully connected', 'success' ) );

}

And the templates/settings-page.php file:

<div class="wrap">
    <h1><?php esc_html_e( 'Settings page title' ); ?></h1>
    <form action="options.php" method="POST">
		<?php settings_fields( 'fake-option' ); ?>
        <h2><?php esc_html_e( 'Add log' ); ?></h2>

        <table class="form-table" role="presentation">
            <tbody>
            <tr>
                <th scope="row"><label for="log_title"><?php esc_html_e( 'Log title' ); ?></label></th>
                <td><input name="log_title" id="log_title" type="url" class="regular-text" required/></td>
            </tr>
            <tr>
                <th scope="row">More fields</th>
                <td></td>
            </tr>
            </tbody>
        </table>
		<?php submit_button( __( 'Add log' ) ); ?>
    </form>
</div>

How it works

We basically add settings like normal, except we skip a few things
First off normally you would add a section using add_settings_section in the same function where we register the settings field(s). But as we don’t really render a settings field in the template, we don’t add it at all. The same goes for the do_settings_sections in the Template.

In the settings template we do have the regular settings_fields this makes sure all the nonce fields are printed. And the submit_button.

Finally the thing we do extra and where the magic happens.
In the sanitize_callback of the register_setting function we set the save_post function.
Normally this is for sanitizing the option you want to save. But in this case we are not saving a option, we are saving a post. In this function we validate all the data send by the form, sanitize the data and save a new post.

And when the data is validated and saved we send a success notice with the add_settings_error function. Yes, we send a success notice with the **_error function. It can be done with the final argument, by setting the type to ‘success’.

Handling multiple custom options.

In the project that sparked this post I need to create a post, but also list those posts with a delete button.
What I found useful was creating a second register_setting, put them in two separate <form> tags on the template and let one handle the creating and the other the deleting.

<?php
function register_settings() {
	register_setting(
		'fake-option-create',
		'fake-option-create',
		array( 'sanitize_callback' => 'save_post' )
	);
	register_setting(
		'fake-option-delete',
		'fake-option-delete',
		array( 'sanitize_callback' => 'delete_post' )
	);
}
function save_post()  {
    // handles saveing/creating.
}
function delete_post() {
    // handles deleting.
}
?>


<div class="wrap">
    <h1><?php esc_html_e( 'Settings page title' ); ?></h1>
    <form action="options.php" method="POST">
		<?php settings_fields( 'fake-option-create' ); ?>
        HTML FORM HERE
		<?php submit_button( __( 'Add log' ) ); ?>
    </form>
    <form action="options.php" method="POST">
		<?php settings_fields( 'fake-option-delete' ); ?>
        LIST POSTS HERE WITH CHECKBOXES
		<?php submit_button( __( 'Delete logs' ) ); ?>
    </form>
</div>

Other tweaks.

?php_version=7.4&platform=wordpress&platform_version=5.6&software=free&software_version=15.6.2&days_active=30plus&user_language=en_US

  • Create, delete and updates posts (and post meta)
  • The same for user(meta and tax(meta)
  • External API calls.
  • Mix in regular use of the settings API.
  • World peace.

Mount a USB drive on Raspberry Pi

SD cards can hold a lot of data these days. I bought a 64GB micro SD for €15 the other day. While that is more then enough for the OS and most work, it’s not enough for my backups.

SD cards can hold a lot of data these days. I bought a 64GB micro SD for €15 the other day. While that is more then enough for the OS and most work, it’s not enough for my backups.
So in this guide we are mounting a 4TB external hard drive with a USB cable.

Sidenote, If you have a new drive I recommend to format it as ext4, that is not part of this tutorial. It’s easier to do on a your main Latop/Computer with an interface.

Boot up your Pi, login and plug in your drive.

Find the USB drive before mounting

First we run the command: sudo blkid
The output should look like this.

List of drive
List of available drives

This is a list of all avialable drives. The first , the /dev/mmcblk0 are part of the Rasberry itself. External drives should be like /dev/sda or similar. In my case it’s also easy to identify by the label.
The important thing to note is the UUID. Copy that, we will need it in the next steps.

Mounting the drive

Before we mount the drive it’s important to know where we are going to mount the drive. Traditional places are /media/* and /mnt/* So we will create a directory inside there:

sudo mkdir /media/4TB

Finally we simply mount the directory.

sudo mount /dev/sda /media/4TB

And that’s it.

The result before and after mounting a drive
Before mounting the folder is empty, after you see the folders I already had on the drive.

Mount a drive on startup

The method above is ease, but has one major downside. If the Pi ever is rebooted you will have to manually mount again.
Try it out now: sudo reboot now and log in again, and check your folder, it should be empty.

Before we start we need the UUID from the drive. You can find it in with this command: sudo blkid
Now open the fstab file where all mount points are stored.

sudo nano /etc/fstab

Inside add the following line, replace the UUID and optinal the path where you want to mount your drive. I’m mounting it at /media/4TB
I’m also amusing the drive is formatted ext4, if not adjust that accordingly

UUID=12272d4a-c58e-4b32-a908-ed8bf737db54    /media/4TB   ext4    defaults,errors=remount-ro 0       1

To test that is works you can mount it with: sudo mount -a
If that works reboot the system and check again: sudo reboot now

For more details about the fstab and it’s options check this this fstab page

Install Let’s Encrypt SSL on Raspberry Pi

I love Let’s Encrypt. It’s free SSL, it’s safer because of the auto-renewal and it’s so easy to setup. No more emailing around validating company name and all that hassle.

I love Let’s Encrypt. It’s free SSL, it’s safer because of the auto-renewal and it’s so easy to setup. No more emailing around validating company name and all that hassle.
Before you start you do need to have opened your router to the outside world.

The installation on server level has also got a lot easier in the past years. The following will work for both Raspbian and Ubuntu.
For other systems check the official site, just select the correct OS and webserver.

Install Certbot

Let’s Encrypt will work through a program called Certbot. We will install Certbot and that will get the free SSL certificates from Let’s Encrypt.

To install run these 2 commands, these might take a while:

sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot

Once this is done we can start getting the SSL-certificates. Run:

sudo certbot --nginx

Since it’s the first time running it will ask for email and some other stuff.

The first setup of Certbot
The first setup of certbot

For the next step you can select specific domains, or all.

The first setup of Certbot
The first setup of certbot

It should create and set all certificates correctly. If in the future you add a new domain just rerun the command sudo certbot --nginx and select the new domains.

Auto renewal

Let’s Encrypt certificates are valid for 90 days, this is for security reasons. This does mean they need to get renewed periodically.
By default Certbot should have installed auto renewal. But we are going to check it with the command: systemctl list-timers --all

Out put of command systemctl list-timers --all
Checking Timers for certbot auto renewal

There should be some output about snap.certbot.renew.timer.
If not we are going to setup the renewal ourself.

The command to renew the certificates is sudo certbot renew
You can also run this command by itself to test it out.

As it is run by root we will also add it to the crontab of the root user.

sudo crontab -e

Inside we add the following command.

0 0 * * 1 certbot renew

Here we set it to run every Monday at 0:00 (so Sunday to Monday night)
That’s more then enough, you can run it more if you want, I would not run it less.

Test your SSL connection.

Finally go to ssllabs.com and test your site. Behind the scenes Certbot also adds a bunch of security settings which will help get that A+.

SSLlabs results for janw.me
The results for janw.me on ssllabs.com

Installing WordPress Coding standards Globally

When possible I like to code using the WordPress coding Standards. I always set my PHPstorm coding style to WordPress. And when I start a new plugin I implement the PHPCodeSniffer rules for WordPress.
In the most ideal scenario you can include these in your composer settings, but that’s not always possible so it’s handy to have them installed globally.

To install run these 2 commands:

composer global require --dev squizlabs/php_codesniffer wp-coding-standards/wpcs phpcompatibility/phpcompatibility-wp dealerdirect/phpcodesniffer-composer-installer wptrt/wpthemereview
composer global update --dev --with-dependencies

Now all you need is installed. In your global composer directory. You can get that running: composer config home -g
By default is should return `~/.config/composer`

To test the phpcs with the global dir I run: ~/.config/composer/vendor/bin/phpcs -i
The output should look like this:

Result of command is "The installed coding standards are PSR12, PEAR, PSR2, PSR1, Squiz, MySource, Zend, PHPCompatibility, PHPCompatibilityParagonieRandomCompat, PHPCompatibilityParagonieSodiumCompat, PHPCompatibilityWP, WordPress-Docs, WordPress, WordPress-Extra, WordPress-Core and WPThemeReview"
Installed phpcs rulesets

Running this whole command will get annoying and will cluter up the terminal. So we will put the bin directory in the global $PATH
The bin directory we already got. (but we can also get using composer global config bin-dir --absolute)

To your ~/.bashrc add:

export PATH="$HOME/.config/composer/vendor/bin:$PATH"

Open a new terminal and test the command phpcs -i it should now work globally and is ready to run on individual projects.

Sources:

Install Ubuntu on Raspberry Pi

This guide will only cover the Ubuntu server part, so not the desktop.At the end of this guide you will have a headless Raspberry Pi running Ubuntu that you can login with ssh.

Let me state a few things before we start.
This guide will only cover the Ubuntu server part, so not the desktop. There are other guides that do cover that.
At the end you will have a headless Raspberry Pi running Ubuntu that you can login with ssh.

Download and Flash

From the official downloads page download the Raspberry Pi Imager. Install and run it. Select the latest LTS version of Ubuntu, select 64 bit for the Pi 3 or Pi 4.
You could install the latest version but I like that the LTS versions are supported for 5 years.

The main screen of the Raspberry Pi Imager application
Raspberry Pi imager
Selecting Ubuntu using the Raspberry Pi Imager
Selecting Ubuntu using the Raspberry Pi Imager

After the SD card is done

When the writing of the SD card is done. Create a folder named ssh in the root of the system-boot partition.

If you need wifi I suggest this guide, I don’t use wifi on my Pi’s so I haven’t tried.

Then plug in the SD card and powerup. Do wait 2~5minutes before actually trying. Ubuntu has to setup stuff for the first time. If after 20max you still can’t find an IP. The flashing of the SD card has gone wrong and you’ll have to restart.

Finding the IP-address

First we need to get the IP-address that the Pi is running on. There are multiple ways to find the IP, you could login to your router and check for the device. It’s hostname is probably “ubuntu.home“, “ubuntu.local” or plain “ubuntu”. Usually the it will start with 192.168.x.x

There is an official guide for finding the IP-address. It’s one of the more annoying things when you are new. I myself use nmap

First login in Ubuntu

Once you do have the IP-address login with the command ssh ubuntu@IP-HERE

Loggin in on Ubuntu for the first time
Login in on Ubuntu for the first time

The password is ubuntu
You will be asked to replace the default password with a new one. After changing it login again.

Default login for Ubuntu on Raspberry Pi
Default login for Ubuntu on Raspberry Pi

Now we can login we will change some things, some are optional.

Updates

For complete updates run: sudo apt update && sudo apt upgrade -y
This might take a while. When this is done for the first time you might have to reboot, I recommend rebooting anyway: sudo reboot now

Hostname

The default hostname is ubuntu, that might be fine but I’d rather give it a name that is more descriptive of it’s purpuse. Check the current hostname with the command hostname it should be ubuntu

For my example I will update the hostname to raspberry-webserver
Run the following command with the name you want.
sudo hostnamectl set-hostname raspberry-webserver

Then Reboot: sudo reboot now

Changing the hostname
Changing hostname

Static IP-address

By default the Pi gets a dynamic IP, the main benefit of a static IP is that it never changes. This might depend on your router.
With this static IP I know I can always login with the same IP.

Before we start a few notes. This part is always a bit annoying.

  • I followed 2 guides for this section, check them for more information.
    itzgeek & serverlab
  • These example only works when an ethernet cable is attached, not on wifi.
  • If you can’t login after applying the changes pull the plug and stick the SD card in your computer and delete /etc/netplan/01-static-ip.yaml it can be found in the writeable partition.
  • Depending on your router the 3rd number of the IP might differ, so it might be 192.168.1.161 it should be the same as the 3rd part of what you use to login originally.

Set the IP

In this example I will set the static IP to 192.168.2.161
First we need the default IP of the router. We need it for the gateway. Run ip route show

Running ip route show
Default ip here is 192.168.2.254

This output shows “default via 192.168.2.254” That’s the IP we need.

We will create an extra netplan config file: sudo nano /etc/netplan/01-static-ip.yaml And match this content. Make sure you replace the IP with your own.
This is a yaml file so the number of spaces/tabs matters.

network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: no
      addresses:
        - 192.168.2.161/24
      gateway4: 192.168.2.254

Next we reload netplan to apply the configuration: sudo netplan generate && sudo netplan apply This will likely freeze your terminal. Close it and login in a new terminal tab.

Once you can login with the new IP it’s done.

Only allow ssh key login on Raspberry Pi

Having ssh open on a raspberry Pi can be dangerous. To make it more secure we will setup SSH keys and disallow passwords.

This is the followup of Opening a Raspberry Pi to the outside world On the internet bots constantly try to hack servers. The Raspberry Pi is popular target. That’s why by default ssh is disabled nowadays.
To make this more secure we will only allow login by ssh key instead of a password. Ssh keys are a lot safer and you also won’t need to type/paste the password on login.

⚠️ Always test in a new terminal tab/window.
This guide has the potential to log yourself out of your pi.
If you always keep one loggedin window open you can always revert everything there.

Generate ssh keys

Generate keys on the Raspberry Pi: ssh-keygen -t rsa -b 4096 -C "pi-webserver"
Just press enter for both the location and password.
This key can be used for git authentication, log in to other machines and more. You might never need them, but it never hurts.

Add your own ssh key

On your own local machine also generate ssh keys. Check this github guide.

Once you’ve your own ssh keys. login on the pi and open File: nano ~/.ssh/authorized_keys

Paste your public key here and save.
For linux/mac you can find your public key with: cat ~/.ssh/id_rsa.pub

Open a new terminal window and try to login.

Disable Passwords

Open file: sudo nano /etc/ssh/sshd_config

Look for PasswordAuthentication
remove the # at the front and change the value to no.

PasswordAuthentication no

As a fall-back we will allow login in from the home network. So at the bottom of the file add. (the 4 spaces in front of the line matter)

Match address 192.168.*.*
    PasswordAuthentication yes

Restart the ssh service. sudo service ssh restart

Try to login in a new window. you should not need to enter a password.
If that works try login in outside of your home network easiest is probably to connect a smartphone tethering.

Raspberry Pi basic installation

This guide will start help you set up a Raspberry Pi with Raspberry Pi OS (Rasbian). After that, some basic configuration and updates.

This guide will help you set up a Raspberry Pi with Rasbian. Ready to do pretty much whatever you want.

What hardware do you need?

a Raspberry Pi, 4B a case, a network cable, power supply and an SD-card.
The Required Hardware
  • First off a Raspberry Pi of course, this tutorial uses a 4B 4GB. And should also work on a Model 2B and 3B.
  • Second I have a network cable. Wifi is available on since the 3B but wire always has a more stable connection, I will use a wire.
  • A USB-C power source. This can get a bit tricky. Recommended is at least 2A with 5V. A normal smartphone adapter won’t give that. Using less will slow down your Raspberry. If you’re using a 2B or 3B you need micro USB instead of USB-c
  • A micro SD card, at least class 10 for speed and with 8GB or more. The SD card will serve as the main harddrive for the pi. An micro SD adapter is also needed to install the image on the SD card.
  • A Case. With the Pi4 I’d recommend one with a builtin fan, as it can get to hot for itself.

Installing Rasberry Pi OS Lite

In the past this was called Rasbian. I liked that name much better….
I also have a guide for installing Ubuntu on the Pi.

This guide will start help you set up a Raspberry Pi with Rasbian. And basic Pi configuration.

The main screen of the Raspberry Pi Imager application
Raspberry Pi imager
Selecting Raspberry Pi OS Lite with the Pi Imager
Select Raspberry Pi OS Lite
Progress of writing the image with the Raspberry Pi Imager
Almost done writing the image.

Flashing the image can take a while. You could set up all the wires and put things in place.

After the SD card is done

When the writing of the SD card is done. Create a folder named ssh in the root of the boot partition.

Also in the boot partition is a file called cmdline.txt. Open that, and at the end of the line add the IP you want to give the Pi. Usually you’re restricted to 192.168.1.x or 192.168.2.x but that depends on your router settings.

 EXTSTING_SETTINGS=SETTINGS ip=192.168.x.x

If you need wifi place a file named wpa_supplicant.conf also in the boot partition. And add the following:

network={
    ssid="YOUR_SSID_WIFI_NAME"
    psk="YOUR_PASSWORD"
    key_mgmt=WPA-PSK
}

I haven’t tried this myself so more information here, The static IP probably won’t work with wifi.

Then plug in the SD card and powerup. Do wait 2~5minutes before actually trying. The Raspberry has to setup stuff for the first time. If after 20max you still can’t find an IP. The flashing of the SD card has gone wrong and you’ll have to restart.

Go to your own computer and open the terminal (or Putty for the windows users) Login with the command ssh pi@192.168.*.* Use the IP address you noted before. If it asks if you want to continue choose yes. and use the default password: raspberry Putty again will work a bit different.

Login in on a Raspberry Pi by a terminal
Logging in for the first time.

Global configuration

Now you are logged-in remote on the raspberry pi. Time to configure some stuff with the command sudo raspi-config.
On this menu we can configure some basic settings. Let’s change some settings.

the start screen of raspi-config tool
Start screen of the raspi-config screen.
  1. Change User Password CHANGE THE PASSWORD. Longer is better and safer
  2. Network options
    N1 Hostname the name of your Raspberry in your network. I named mine pi-webserver. Not required. It’s just a label. But useful if you got multiple Raspberry’s running.
  3. Boot options
    B1 Desktop / CLI Chose the ‘Console Autologin’ or ‘Console’ option. Don’t choose auto login if someone else might have physical access to the pi. Desktop will just waste power.
    B2 Wait for Network at Boot Just turn this off to be safe.
  4. Localisation Options Here you can change the timezone to your current one. You could change the language of the PI and the keyboard layout if you need to. Keep in mind I will be using English.
  5. Interfacing Options we don’t need this
  6. Overclock Only for the Pi 2B, Set this to the highest setting. It won’t hurt and will make the Pi a faster.
  7. Advanced Options This will be a bit more.
    A1 Expend Filesystem Run this. No need to reboot immediately.
    A3 Memory Split Set this to 16. Because we don’t have a interface we won’t need memory for that.
  8. Update Run this is should not hurt. It’s only to update the raspi-config tool.

When this is finished the pi might download some language packs. It it doesn’t reboot. you have to do it yourself. sudo reboot now.

Wait about a minute and login in again like before. But with the new password

Updates

This might take a while but a fresh install it’s highly recommended to update.

sudo apt update && sudo apt upgrade -y

Now we update all default installed software. If the image is old or hasn’t been updated in a while it might take a long time. The date of the image is in the file name of the downloaded image.
At times It might appear to hang. It Unless it’s stuck on one thing 15 minutes it’s probably fine. Give it an 15 to 30 minutes. Else pull the plug.

When it’s done reboot again just to be sure: sudo reboot now

You now have a Raspberry Pi ready for use.