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

Assume that the script to be run, when the startup, is at /home/transang/startup.sh.

Create systemd startup script (most reliable)

Create systemd unit file with a .service extension in /etc/systemd/system, for example 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

Now, you can start (or stop) this service with systemctl start my-service.service (or systemctl stop my-service.service).
To start this service when startup, run systemctl enable my-service.service only once.

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

Add startup crontab job (unreliable)

Replace the repeat section of crontab definition with @reboot.

For instance: after executing crontab -e, add this line @reboot /home/transang/startup.sh.

Nonetheless, this approach is not reliable. There is also a bug in some Debian variants. The behavior is inconsistent between reboot and shutdown/start. Thus, I will not discuss further on the timing when this crontab is executed, where it is stored, who the user is running it, or whether this job is executed with a sudoer privilege.

Create a shell startup script (inconsistent)

You can refer to this post to understand startup scripts and their execution order.

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