How to customize Jetson nano image

You would not normally need to name the rootfs directory (with “-R”) if the location is the default. You could skip naming this in your case.

The permissions of the sudo file were wrong. However, although this is normally because of losing permissions on a non-ext4 filesystem, it looks like this is already correctly ext4. I don’t know how it ended up being wrong unless there was intermediate storage on a non-Linux filesystem and then copied to the Linux filesystem. Was there any manual copying of rootfs content from one place to another prior to flash?

Normally, if some intermediate non-Linux filesystem is used, then several subtle but important permissions will be altered (and fixing this is not so easy). In your “Linux_for_Tegra/rootfs/usr/bin/”, if you cd there, and then run “ls -l sudo”, what shows up? Whatever is there is what should transfer to the Jetson. If this is also wrong, then you’ll have to delete and reinstall that content and flash again. If it is a valid permission, then something else went wrong which is very peculiar.

I forgot to mention, if you have a temporary mount location, e.g., “mkdir ~/image”, then from the “bootloader” directory you can:
sudo mount -o loop ./system.img.raw ~/image


cd ~/image/usr/bin
ls -l sudo
sudo umount ~/image
sudo losetup -D

Is this permission different? This is the actual image that was flashed, which in turn was based on “rootfs/”.

Yes. As I don’t want to mix my updating with the original files created by SDK manager, I copied the folder of Linux_for_Tegra to another place in the same host PC.

The sudo issue is my mistake on copying. I have sorted it by add -p and -R to cp command.

Now, my question is that the has no way to set default country, language, time zone. How can I setup them before flashing the image? or which files will be modified before flashing?


The only files which flash will modify are possibly in “/boot”, “/lib/firmware” (not likely, but possible), or “/lib/modules”. Anything else you modify in “rootfs/” will remain just as you modify it.

Incidentally, cp can alter user ID and group ID during copy if it is from

One of the best things you can do is to manually set each requirement you have, and then clone, followed by using the clone itself in “rootfs/” (you’d loopback mount it or copy with something like rsync). One can either directly use the clone (as “bootloader/system.img”), or use it to replace “rootfs/” (which generates “bootloader/system.img” if “-r” is not used in command line When used with “-r” as “system.img”, no changes are ever made; when used to replace “rootfs/”, then generated images are 100% the same except for options such as kernel and device tree specified by the target. This is the most comprehensive way to get exactly what you want.

Related to this is the content in “/etc/skel”. When a new user is created this is the “skeleton” of what goes into the new home directory. For example, if you have a “~/.bashrc” you’ve customized (such as language/locale), and place it in “/etc/skel”, then commands to create a new user will cause each new user to have that “~/.bashrc” file.

Everything needed for country, character encoding, so on, can get very complicated if non-default. However, much is contained in some “~/.bash...something...” file (especially “~/.bashrc”). Some is also in “/etc”, usually related to default search paths and some system configurations. You’d have to spend a lot of time reading “man bash” to get an overview of what is going on, but here are some files in “/etc” related to bash, which in turn is much of your “user environment”:

  • /etc/bash.bashrc
  • /etc/bash.bash.logout
    …and a list of some home directory bash files:
  • ~/.bash_profile
  • ~/.bashrc
  • ~/.bash_logout
  • ~/.inputrc
    (mostly taken from “man bash”)

If you want to see what environmental settings there are (without regard to the file they come from), then run command “printenv” (some of which is for a current login X environment setup, and so those are always generated from scratch and you wouldn’t edit those yourself, but many can be altered in “~/.bashrc” or one of the “/etcbash files).

Note that if “/etc/skel” content is set up with the locale and country and other content you want, then creation of the home directory won’t require changing The command to create a home and the commands to set up locale are actually independent of each other.

It is of special interest and very important to know is that the root filesystem in the Jetson is not the root filesystem of the host PC. Even so, normally cp will consider any file found and copied to be part of the local computer’s files. Owners and groups are actually numeric, and can differ between host PC and Jetson. Any time you copy a file which is owned or group ID different than what the host PC uses, cp will translate the new file ownership to one from the host PC. Thus when it is copied verbatim to the Jetson, this will now be wrong. A big reason to copy filesystems with rsync is that it has the option to preserve numeric user ID and numeric group ID. If you want better rsync commands to copy with just ask, but here is an example (contrived locations and file names):

rsync --dry-run -avcrltxAP --info=progress2,stats2 --numeric-ids --exclude '.gvfs' --exclude 'lost+found' /mnt/temporary_source ~/destination`

(in the above “--dry-run” tells it to say what it would do, but not really do anything…it is a way to test safely; remove “--dry-run” and it will really do this; the “--info” content makes it verbose; all kinds of “preservation” options are included; one would want to exclude special directories of which “lost+found” and “.gvfs” are)

In your case you lost suid, and one reason this can be lost is by not using “sudo” for the copy (only root can copy suid/guid bits).

Regarding timezone, examine (these are usually manipulated via commands and not direct file edits; examine these on a Jetson you have already set up for the country and timezone you want):

  • ls -l /etc/localtime
  • cat /etc/timezone
  • Indirectly, “cat /etc/adjtime
    (all of which can be put in “rootfs/etc/” prior to creating an image, or will be preserved if you flash with “-r” and a clone)

I have a question on i2c. It does not available in Linux_for_Tegra/rootfs/dev/ before flash to Jetson nano.
After flashed Jetson nano and boot up, the i2c-0, i2c-1, i2c-101, i2c-2, …, i2c-8 are listed in the folder of /dev. But all of them are in root group, instead of i2c group. No i2c group is available. I manual added i2c group by

sudo groupadd --system i2c

add user to i2c group
and changed the i2c- dev group from root to i2c by

sudo chown root:i2c /dev/i2c-*

I checked, its group has been changed to i2c

crw------- 1 root i2c 89, 0 Sep 23 07:45 /dev/i2c-0

I also applied the command below to i2c

sudo chmod a+rw /dev/i2c-*

After this, I tested the i2c. It works. But after reboot, the i2c-* changed to root group again.

crw------- 1 root root 89, 0 Sep 23 07:45 /dev/i2c-0

what’s wrong with it? How can I fix it before flash the image into device?


First, files in “/dev” are “device special files” (mostly). Otherwise known as “pseudo” files. They don’t exist on the filesystem, and are really drivers pretending to be files in order to communicate with the world outside of the kernel. It is possible to create such a file manually, but the saying is that “the lights are on, but nobody is home”. Such files do nothing because the driver is not running.

Of the files that are there, it is generally the driver or the udev system which controls their naming and permissions. Using chmod on them might work, but is generally frowned upon even if it does work. The system set them up that way for a reason. Changing a name or permission is normally done by editing a udev rule. Adding them is normally be making sure the driver is running.

When logged in to the Jetson (or to any Linux PC), if you run the command “df -H -T” it will show all mounted filesystems and their types, along with usage information. For you, I suggest running this command to illustrate:
df -H -T / /dev

The above shows both the root of the whole filesystem, which should be type ext4, and “/dev”, which is type “udev”. These are different disks, whereby /dev is virtual. To learn about udev you might look at this tutorial:

Note that in “/etc/udev” that some of the rules there are actual files, and others are symbolic links into system files somewhere in a lib directory’s udev subdirectory. To activate something on the regular unmodified udev setup you would add a symbolic link into the correct “/etc/udev” subdirectory; to modify a rule which already exists, you would instead copy the file to the correct subdirectory of “/etc/udev”, and then edit that file. You could deactivate that file by removing it from the “/etc/udev” subdirectory. You would never edit any of the system files under a “/lib” or “/usr/lib” directory.

Thus, you don’t add those files in the rootfs…they do not yet exist and it isn’t the rootfs which contains them. You would do whatever is needed to make sure the driver is present (the possibility of a “/dev” file existing), and then make whatever arrangement is needed for the driver to see the hardware (which is what triggers actual driver load, and thus the existence of the “/dev” pseudo file).

If a driver is integrated into the kernel, then there is no need to make sure the driver is available. For that case you’d only need to make sure the kernel knows to load the driver. If the driver is in the form of a module, then you’d simply make sure the driver knows how to find the device it works with.

Some devices are “plug-n-play”. Some buses know how to query devices. USB and PCI are good examples. Even a monitor’s HDMI or DisplayPort cable has that ability. Other devices cannot be queried, and the only way a driver knows to load is if it is manually told where to look, along with other arguments to pass to the driver (e.g., a physical address, perhaps a protocol). This latter is not needed for a plug-n-play device, but for many others (including i2c), this is the realm of the device tree.

The official docs mention how to work with the device tree (the docs from your L4T release…see “head -n 1 /etc/nv_tegra_release” to find which L4T you are running; incidentally this is just Ubuntu plus Linux drivers). Then find the docs for that release through this listing of releases:
(incidentally, JetPack/SDK Manager is the installer software, but L4T is what gets installed)

Also, search for “PINMUX” here at the official downloads URL:

The PINMUX spreadsheet has macros you can enable. Then, if all of the settings are unchanged but for the specific dev kit you have, then this would produce the same device tree. If you then edit a parameter, e.g., something for i2c setup, then you could use the modified device tree. That tree is usually part of the flash process, although there are ways to test it and update without flashing in most cases (a simple file copy plus a minor edit to “/boot/extlinux/extlinux.conf” to name a new FDT value…which would point to the alternate name instead of the original file name, preferably from an entirely new boot entry to leave the old one as a backup).

As a checklist:

  • Is the driver available? There would be a “CONFIG_...something...” for that item in “zcat /proc/config.gz
  • Is the device “plug-n-play”? If so, no need to worry, but if not, then you need to use the PINMUX spreadsheet macros to install an edited device tree.
  • Need permissions changed? Then add your user to the right group, or edit the udev rules.