In this post, I will introduce three ways to create a startup script in Ubuntu: systemd (systemctl command), cron job (@reboot directive), and shell startup script (.bash_login, .bashrc, ...).

Suppose that the script to run is at /home/transang/startup.sh.

Method 1 (most reliable): Create a systemd startup service

Create a systemd unit file with .service extension in /etc/systemd/system, for example /etc/systemd/system/my-service.service, with the following content.

[Unit]
Description=My custom startup script
# After=network.target
# After=systemd-user-sessions.service
# After=network-online.target

[Service]
# User=spark
# Type=simple
# PIDFile=/run/my-service.pid
ExecStart=/home/transang/startup.sh start
# ExecReload=/home/transang/startup.sh reload
# ExecStop=/home/transang/startup.sh stop
# TimeoutSec=30
# Restart=on-failure
# RestartSec=30
# StartLimitInterval=350
# StartLimitBurst=10

[Install]
WantedBy=multi-user.target

Lines starting with # can be truncated. Hence, the above unit file is equivalent to:

[Unit]
Description=My custom startup script

[Service]
ExecStart=/home/transang/startup.sh start

[Install]
WantedBy=multi-user.target

You can now start/stop this service with systemctl start my-service / systemctl stop my-service.
To enable this service when startup, run systemctl enable my-service.

Refer to this digital ocean post for more detail on the systemd unit file structure.

Method 2 (unreliable): Add startup cron job

Add a crontab line with the repeat section as @reboot, i.e., run crontab -e and append the following line to the opened file.

@reboot /home/transang/startup.sh

Nonetheless, this approach is not reliable. Not only is there a bug in some Debian variants, but the behavior of the @rebooot cron job is also inconsistent between reboots and shutdowns/starts. Therefore, I will not discuss other perspectives such as when the @reboot cron job gets executed, where it is stored, who the user is running it, or whether this cron job gets executed with a sudoer privilege.

Cron is not always installed by default in some Linux distributions (such as Arch Linux) and requires a manual installation.

Method 3 (inconsistent): Create a shell startup script

You can refer to this related post to read about startup scripts and their execution order.

A side note: after reading the post mentioned above, you will realize that it is unsure whether a bash startup script is executed, when it is executed (is it run before or after you log in to the graphical screen?).
This behavior also varies significantly on the graphical program (gnome, unity, kdm, .., etc.), even from version to version.