Skip to content

Instantly share code, notes, and snippets.

@henri
Last active April 9, 2025 23:27
Show Gist options
  • Save henri/34f5452525ddc3727bb66729114ca8b4 to your computer and use it in GitHub Desktop.
Save henri/34f5452525ddc3727bb66729114ca8b4 to your computer and use it in GitHub Desktop.
start as many instances of a browser as you like (posix compliant)

🔒 SPB (start-private-browser)

This GIST has everything you need to run multiple private web browsers on your system.

💾 Installation

Kick off installer script (you may inspect source below) with the one liner four lines bellow in your terminal :

/bin/bash -c "$(curl -fsSL \
https://gist.githubusercontent.com/henri/\
34f5452525ddc3727bb66729114ca8b4/raw/\
500.spb-install-script.bash)"

Detailed install instructions : Copy and paste the lines above into a terminal and press enter.

Mild testing has been completed on the following operating systems :

  • MacOS
  • Linux Mint

📑 Usage

Once installation is complete. The following commands will get you started with SPB.

Start a new private browser session :

~/bin/start-private-browser.bash

Show SPB help :

~/bin/start-private-browser.bash --help | less

Uninstall SPB from your system :

rm -i ~/bin/start-private-browser.bash

⭐ Usage Examples

Handy wrapper scripts to allow searching within a PrivateBrowser via the command line :

  • DuckDuckGo Search : A Fish shell function allowing multiple quick DuckDuckGo searches.
  • YouTube Search : A Fish shell function allowing multiple quick YouTube searches.

Additional, examples like this are warmly welcomed. If you put together something and would like to share a link to your work, then comment below.

🛡️ Telemetry and Privacy

As you would expect. The SPB project has zero telemetry. As such if you decide to install and use this software nothing is sent back to mothership. Not even usage anylitics!

As the default browser option in SPB is Brave and that project is a telemetry sender ; it is advisable that you read the Brave privacy policy. Brave is an open source project. However, if having any kind of telemetry being sent out is a problem then you should opt to use a different browser. Also, follow and chime into this issue on github with your opinion(s) regrading telemetry opt-out features within Brave.

SPB is hosted on GitHub :octocat: and as such data is recorded ⏺️ as you access this page. At the bottom of this page you will find links to disable and manage the way GitHub handles your data and tracking. Also, the following links are useful with regards understanding the stance of GitHub towards privacy and trust :

Different operating systems and even different LINUX distributions have different stances on privacy and telemetry. As such it is advisable that you check these details for your specific operating system. This project works on a variety of operating systems each with their own particulars relating to privacy and telemetry.

🙋 Why is SPB needed?

The use cases for this script are immense and include simplified day-to-day web browsing, trouble-shooting, testing, load-testing, having multiple tabs which are not related in terms of cookies etc.

When starting a private session within the Safari WebBrowser on MacOS each tab is seperate (the cookies are not shared between tabs). However, most other browsers share the private cookies with all private tabs. One approach is not better or worse. But if you would like to have multiple instances of a browser but not have them sharing all those dirty cookies, then this script allows you to start up all as many private (and seperate) sessions as you need and as your system will cope with in terms of system resources. Have fun loading some private browsers.

May the privacy be with you!

:shipit:



⚠️ Disclaimer

SPB (start private browser) is able to start multiple private browsers. But do not expect more functionaility or privacy from running a browser in incogneto mode. The idea behind SPB is that you can load multiple instances which are somewhat seperated from one and other. Each instance is still running under your user accont and on your system.

In the event additional privcay is required run SPB within a VM or within a continer. If higher levels of privacy are needed, then consider these projects :

If you test it and it works on your operating system leave a comment :) or let me know it is not working. currently no plans to make this work on Microsoft Windows. It may work within the WLS? Give it a try and let me know!

At this stage the script is maily configured by direct editing. At some point perhaps a config file will be a thing?

This project is still in the 🌱 seedling stage (with lots of potetential). If you have suggestions / ideas to improve the way it works, then please leave a comment. If you test this on a particular operating system and it works or is not working for you please also leave a comment so that others know to give it a try or not :)

🌏 External Resources

Development / Testing / Automation

General

#!/usr/bin/env bash
#
# Start many instances of Brave at the same time all within the same linux graphical login
# The general idea behind this script to start a private instance of brave-browser
# with local user data directory set to a directory within /tmp/ (by default)
#
# This also allows you to easily run multiple instances of the browser all with different
# startup options.
#
# This script may be easily modified to work with different Blink based browsers for example
# chromium, vivaldi, opera etc...
#
# Henri Shustak 2024
#
# Released under the GNU GPLv3 or later licence :
# https://www.gnu.org/licenses/gpl-3.0.en.html
#
# Project home page (featuring additional information and easy installation guide) :
# https://gist.github.com/henri/34f5452525ddc3727bb66729114ca8b4
#
# version 1.0 - initial release
# version 1.1 - added a check to make sure brave is installed before doing anything
# version 1.2 - added a basic help menu explaining the functionality (if you call it with -h or --help)
# version 1.3 - added basic support for FreeBSD and MacOS
# version 1.4 - added support for listing the running sessions (just list them via screen - nothing internal keeping track)
# version 1.5 - initial support for passing additional options to brave implimented (note : zero checking of option validity)
# version 1.6 - updates to the help output\
# version 1.7 - added url to download brave if it is not installed
# version 1.8 - added user-agent usage example
# version 1.9 - added additional sanity check
# version 2.0 - addeditional requirement check that screen is installed before doing anything
# version 2.1 - bug fix related to screen detection
#
# show usage information
if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]] ; then
echo ""
echo " SPB or 'start-private-browser' is a wrapper to the brave-browser command."
echo ""
echo " This wrapper allows you to quickly start as many sperate instances"
echo " of the brave-browser as your system has available memory to run"
echo " simultianuusly under a single graphical login."
echo ""
echo " Requirments include gnu/screen and the brave-browser to be installed"
echo " on your system. This script has been mildly tested on gnu/linux mint."
echo " It is possible this will also work on many other posix compliant"
echo " opterating systems. Your miliage may vary."
echo ""
echo ""
echo " Usage Summary : "
echo ""
echo " # open a new private browser instance - optionally pass in a URL"
echo " $ start-private-browser [URL-TO-OPEN]"
echo ""
echo " # show list of private browser instances"
echo " $ start-private-browser -ls"
echo ""
echo ""
echo " Additional Resources : "
echo ""
echo " # additional argument information is viewable via the brave-browser man page : "
echo " $ man brave-browser"
echo ""
echo " # list of additional command line arguments : "
echo " https://support.brave.com/hc/en-us/articles/360044860011-How-Do-I-Use-Command-Line-Flags-in-Brave"
echo ""
echo ""
echo " Example Usage: "
echo ""
echo " # simply start a new private browser"
echo " $ start-private-browser "
echo ""
echo " # the same as above but open the browser page on the passed in URL"
echo " $ start-private-browser \"https://brave.com\""
echo ""
echo " # pass additional argument (proxy in this example) to private browser"
echo " $ start-private-browser --proxy-server=\"http://myproxy.com:9090\" \"https://brave.com\""
echo ""
echo " # pass additional argument (app mode [eg no toolbars] in this example) to private browser"
echo " $ start-private-browser --app=\"https://brave.com\""
echo ""
echo " # start up private browser with multiple URLs"
echo " $ start-private-browser duckduckgo.com wolframalpha.com kagi.com"
echo ""
echo " # start browser with specific window position and open xkcd.com"
echo " $ start-private-browser --window-position=10,10 xkcd.com"
echo ""
echo " # start browser with larger / smaller user interface elements"
echo " $ start-private-browser --force-device-scale-factor=1.25 google.com"
echo " $ start-private-browser --force-device-scale-factor=0.95 google.com"
echo ""
echo " # start browswer with spcific user-agent overide (example is FireFox for Windows)"
echo " $ start-private-browser --user-agent=\"Mozilla/5.0 (Windows NT x.y; Win64; x64; rv:10.0) Gecko/20100101 Firefox/10.0\""
echo ""
echo ""
echo ""
exit 0
fi
# configuration variables
screen_session_prefix="brave-incognito" # prefix of the screen session name
temp_path="/tmp/browser" # location of temporary browser data
# list running instances
if [[ "$1" == "-ls" ]] || [[ "$1" == "-l" ]] ; then
output_list=$(screen -ls | grep .${screen_session_prefix}- | awk '{print $1}')
if [[ "${output_list}" != "" ]] ; then
# output_list is not empty - list the items
echo "${output_list}"
exit 0
else
echo "private brave session was not detected."
exit -88
fi
fi
# check the operating system ; also check on brave and screen availability on system
os_type="$(uname)"
if [[ "${os_type}" == "Darwin" ]] ; then
# running on macOS
start_brave_path="/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
mktemp_options="-d"
if [[ -x "${start_brave_path}" ]] ; then breave_browser_available=0 ; else breave_browser_available=1 ; fi
elif [[ "${os_type}" == "Linux" ]] || [[ "$(uname)" == "FreeBSD" ]]; then
# running on GNU/LINUX or FreeBSD
start_brave_path="brave-browser"
which ${start_brave_path} >> /dev/null ; breave_browser_available=${?}
mktemp_options="--directory"
else
echo "ERROR! : Unsupported operating system."
echo ""
echo " Please note this script requires a POSIX compliant"
echo " or at minimum a POSIX like operating system"
echo ""
echo -99
fi
# report if brave-browser is not available
if [[ ${breave_browser_available} != 0 ]] ; then
echo "ERROR! : Unable to locate brave-browser on your system."
echo ""
echo " Ensure that brave-browser is installed on your system and that the "
echo " correct path is configured within your PATH enviroment variable."
echo ""
echo " Instructions to install brave are available from the URL below : "
echo " https://brave.com/download/"
echo ""
exit -1
fi
# report if screen is not available
which screen >> /dev/null ; screen_available=${?}
if [[ ${screen_available} != 0 ]] ; then
echo "ERROR! : Unable to locate screen on your system."
echo ""
echo " Ensure that screen is installed on your system and that the "
echo " correct path is configured within your PATH enviroment variable."
echo ""
echo " Official instructions to install screen are available from the URL below : "
echo " https://www.gnu.org/software/screen/"
echo ""
echo " Non-Offical instructions to install screen on LINUX are available from the URL below : "
echo " https://linuxtldr.com/installing-screen/"
echo ""
echo " Non-Offical instructions to install screen on FreeBSD are available from the URL below : "
echo " https://joshdawes.com/installing-screen-for-freebsd/"
echo ""
exit -1
fi
# create a tmporary directory and setup the permissions so that only your user has access to the direcotry
browser_tmp_directory=$(mktemp ${mktemp_options} ${temp_path}-$(whoami)-XXXXX)
if [[ $? != 0 ]] || [[ ! -d ${browser_tmp_directory} ]] ; then
echo "ERROR! : Unable to create temporary user data directory"
exit -2
fi
chmod 700 ${browser_tmp_directory}
if [[ $? != 0 ]] ; then
echo "ERROR! : Unable set permissions correctly for temporary user data directory"
exit -3
fi
# parse the arguments for options and URL's to pass to brave.
brave_options="--user-data-dir=${browser_tmp_directory} --incognito"
url_list=""
while [[ ${#} -ge 1 ]] ; do
# note, that no additional checking for validy of options is performed.
# maybe in a future version of this script.
if [[ $(echo "${1}" | grep -e "^-") ]] ; then
brave_options="${brave_options} ${1}"
else
url_list="${url_list} \"${1}\""
fi
shift
done
# start a screen session with the name based off the temp directory, then once brave exits delete the temporary directory
screen_session_name="${screen_session_prefix}-$(echo "${browser_tmp_directory}" | awk -F "${temp_path}-" '{print$2}')"
screen -S "${screen_session_name}" -dm bash -c " \"${start_brave_path}\" ${brave_options} ${url_list} ; sleep 1 ; sync ; rm -rf ${browser_tmp_directory} "
exit 0
#!/usr/bin/env bash
#
# (C)Copyright Henri Shustak 2025
# Licenced Under the GNU GPL v3 or later
# https://www.gnu.org/licenses/gpl-3.0.en.html
#
# Simple build script to install
# the start-private-browser.bash
# and setup an alias within your
# shell - automatical styles !!!
#
# This script downloads and the
# files from this URL :
# https://gist.github.com/henri/34f5452525ddc3727bb66729114ca8b4/
#
# Once this script has successfully exectued, start a private browser with this command :
# ~/bin/start-private-browser.bash
#
# Initial release only supports zsh, fish and bash shell alias printing.
# Pull requests for other shell alias support bug fixes / reports welcome.
# I will add support for additional shells and further automation as time permits.
#
# version 1.0 - Initial Release
# version 1.1 - Added BASH shell alias setup output
# version 1.2 - Shallow clone (seems like a good idea)
# version 1.3 - Improved output somewhat specific to insalled shells displayed
# version 1.4 - Implimented additional error detection prior to temporary build directory deletion
# version 1.5 - Added enviroment variable to skip file overwriting (useful for automated upgrades)
# version 1.6 - Minor bug fix
# version 1.7 - Added ZSH shell alias setup output
# version 1.8 - Basic version reporting added
# version 1.9 - Implimented additional error checking and handling of unexpected conditions
# version 2.0 - Added additional output regarding the path of the file which if this is an upgrade
# version 2.1 - Dependency check added for git
#
# setup enviroment varibles
if [[ "${SPB_SKIP_OVERWRITE_CHECK}" != "true" ]] ; then
# SPB_SKIP_OVERWRITE_CHECK enviroment varible not detected ; set to false (default)
SPB_SKIP_OVERWRITE_CHECK="false"
fi
which git >> /dev/null ; git_available=${?}
if [[ ${git_available} != 0 ]] ; then
echo "ERROR! : The git command was detected on your"
echo " system. Ensure it is part of your"
echo " path or install git onto your"
echo " system and try running this"
echo " installer again."
exit -99
fi
# create a build directory, we will be sucking down the latest version of
# everything from git into this directory, then to clean up, we can delete :)
cd $( mktemp -d /tmp/spb-build.XXXXXXX )
if [[ $? != 0 ]] ; then
echo "ERROR! : Unable to setup the temporary build dirctory!"
exit -99
fi
# double sanity check (not needed) but lets avoid deleting anything important by mistake when we clean up
temporary_build_directory="$(pwd)"
if ! [[ $(echo "$(basename ${temporary_build_directory})" | grep "spb-build") ]] ; then
echo "ERROR! : Unable to succesfully locate the temporary directory"
exit -99
fi
# report the temporary build directory :
echo ""
echo "This script has created a temporay build directory : "
echo "$(pwd)"
echo ""
# clone a copy of the latest version into the temp directory
echo "Downloading latest version of SPB (start-private-browser)..."
git clone --depth 1 --single-branch --branch=main https://gist.github.com/henri/34f5452525ddc3727bb66729114ca8b4 start-private-browser-latest
if [[ $? != 0 ]] ; then
echo ""
echo "ERROR! : Sucking down latest version from git!"
echo " It is likely that git is not installed on"
echo " this system or there is a problem with"
echo " network access or possibly GitHub has"
echo " crashed or has been blocked?"
echo ""
echo " I am sure you will sort it out!"
exit -98
fi
# preform exit if we hit an error ; in addition set an EXIT trap
trap 'echo "" ; echo "Something went horribly wrong! Sorry please try again later." ; echo ""' EXIT
set -e
# enter the local repo
cd start-private-browser-latest
downloaded_version=$(grep -E "^# version " ./050.start-private-browser.bash | tail -n 1 | awk '{print $3}')
# check if there is already a version installed?
if [[ -f ~/bin/start-private-browser.bash ]] ; then
echo ""
echo "////////////////////////////////////////////////////////"
echo "WARNING! : Looks like you have a copy already installed."
echo "////////////////////////////////////////////////////////"
installed_version=$(grep -E "^# version " ~/bin/start-private-browser.bash | tail -n 1 | awk '{print $3}')
echo ""
echo " Installed version : ${installed_version}"
echo " Downloaded version : ${downloaded_version}"
if [[ "${SPB_SKIP_OVERWRITE_CHECK}" != "true" ]] ; then
echo ""
echo " Existing copy path : ~/bin/start-private-browser.bash"
echo ""
echo -n " Should we continue and overwrite the existing copy? [y/N] : "
read overwrite_existing
if \
[[ "${overwrite_existing}" != "y" ]] && \
[[ "${overwrite_existing}" != "Y" ]] && \
[[ "${overwrite_existing}" != "yes" ]] && \
[[ "${overwrite_existing}" != "Yes" ]] && \
[[ "${overwrite_existing}" != "YES" ]] \
; then
echo ""
echo "Understood.. Install aborted. You selected to not overwrite the existing version :"
echo " ~/bin/start-private-browser.bash"
echo ""
# clear EXIT trap and exit (with error) - installation did not complete succesfully.
trap - EXIT ; exit -1
fi
else
echo "WARNING! : SPB_SKIP_OVERWRITE_CHECK = true"
echo "WARNING! : Overwriting existing file : ~/bin/start-private-browser.bash"
fi
echo ""
else
echo "Downloaded for installation : SPB version : ${downloaded_version}"
echo ""
fi
# *** TODO *** : put in checking version and then just allow upgrades - that could do with some more work sometime
# copy file into place - assumes you have write access to your home directory.
mkdir -p ~/bin/
cp 050.start-private-browser.bash ~/bin/start-private-browser.bash
chmod +x ~/bin/start-private-browser.bash
echo "File install location : ~/bin/start-private-browser.bash"
echo ""
echo "SPB (Start Private Browser) has been successfully installed!"
echo ""
# prevent exit if we hit an error and remove the EXIT trap
set +e
trap - EXIT
# show alias commands (various shells) - if we do some shell detection could run this command automatically.
command_header_printed="false"
function print_command_header {
if [[ "${command_header_printed}" == "false" ]] ; then
echo " Enter the appriate command for your shell(s) to setup alias"
echo ""
command_header_printed="true"
fi
}
if $(which fish >/dev/null) && [[ ! -e ~/.config/fish/functions/start-private-browser.fish ]] ; then
print_command_header
echo " - fish"
echo " alias -s start-private-browser=\"~/bin/start-private-browser.bash\""
echo ""
fi
if $(which bash >/dev/null) && ! $(grep --silent start-private-browser ~/.bash_alias 2>/dev/null) ; then
print_command_header
echo " - bash"
echo " echo alias start-private-browser=\"~/bin/start-private-browser.bash\" >> ~/.bash_alias && sync && . ~/.bash_alias"
echo ""
fi
if $(which zsh >/dev/null) && $(ps -a | grep --silent zsh) && ! $(grep --silent start-private-browser ~/.zsh_alias 2>/dev/null) ; then
print_command_header
echo " - zsh"
echo " echo alias start-private-browser=\"~/bin/start-private-browser.bash\" >> ~/.zsh_alias && sync && . ~/.zsh_alias"
echo ""
fi
# clean up
cd /tmp/ && rm -rf ${temporary_build_directory}
exit ${?}

RoadMap

  • More testing
  • Additional documentation.

SandBoxing

  • FileSystem User NameSpaces
    • Restricting Access (eg FireJail etc..) Detroyed on exit.

Possible Features

  • An option to store the directory in memory rather than on disk at all?
  • An option to disable cache being written to disk :
    • --disk-cache-dir=
    • --disk-cache-dir="$XDG_RUNTIME_DIR/this-instance-cache"
    • --disable-application-cache --media-cache-size=1 --disk-cache-size=1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment