Automated Raspberry Pi image builder that creates ready-to-deploy Twingate Connectors.
- Automatic Provisioning: Connector creates itself via API on first boot
- Pre-configured Image: Twingate Connector package pre-installed
- Auto-Updates: Configurable automatic updates via unattended-upgrades
- Raspberry Pi OS (Full or Lite) (64-bit) base
- Pre-installed Twingate Connector package
- First-boot provisioning service
- Systemd service configuration
- Configurable auto-update system
- Comprehensive logging
Download the latest pre-built image from Releases
Use Raspberry Pi Imager or Balena Etcher:
# On macOS/Linux with dd (alternative to GUI tools)
sudo dd if=twingate-connector-pi.img of=/dev/sdX bs=4M status=progress
syncMount the boot partition and create your configuration. This can be done by plugging the SD card back in after flashing.
Rename twingate-config.txt.example to twingate-config.txt then open the file in a text editor.
Required Configuration:
TWINGATE_NETWORK=yourcompany
TWINGATE_API_KEY=your_api_key_here
TWINGATE_REMOTE_NETWORK_ID=your_remote_network_idGet API Key:
- Log into the Twingate Admin Console
- Go to Settings > API
- Click "Generate Token"
- Give it a name and
Read, Write, Provisionpermissions and click "Generate" - Copy the token into your config file. This is the only time the Admin Console will show it to you
Get Remote Network ID:
- In the Admin Console, go to Networks
- Click on your Remote Network
- Copy the ID from the URL (e.g.,
https://{yourcompany}.twingate.com/networks/{remoteNetworkId}or similar format)
Insert your SD card into Raspberry Pi, plug in an ethernet cable and power cable. The Connector will:
- Read configuration from boot partition
- Call Twingate API to create Connector
- Generate and retrieve access tokens
- Configure and start the Connector service
- Set up auto-updates (if enabled)
- Restart the Pi
This process can take a few minutes. If 10 minutes goes by and you still don't see the Connector in the Admin Console, check the troubleshooting guide.
Default Credentials:
Username: pi
Password: raspberry
SSH is enabled by default. Important: Change the password after first login:
passwdCheck provisioning status:
# View log on boot partition
cat /boot/firmware/twingate-provision.log
# Or SSH into Pi and check
sudo journalctl -u twingate-firstboot
sudo systemctl status twingate-connectorSSH is enabled by default with these credentials:
Username: pi
Password: raspberry
To disable SSH before first boot, remove the ssh file from boot partition. This can be done when you configure the Connector by deleting the ssh file on the SD card.
Or disable after boot:
sudo systemctl disable ssh
sudo systemctl stop sshAfter first login, improve security:
# Change password
passwd
# Or set up SSH key authentication
mkdir -p ~/.ssh
nano ~/.ssh/authorized_keys
# Paste your public key
# Disable password authentication
sudo nano /etc/ssh/sshd_config
# Set: PasswordAuthentication no
sudo systemctl restart ssh# Required: Your Twingate network name
TWINGATE_NETWORK=yourcompany
# Required: API key from Admin Console
TWINGATE_API_KEY=your_api_key_here
# Required: Remote Network ID to deploy Connector to
TWINGATE_REMOTE_NETWORK_ID=your_remote_network_id
# Optional: Custom Connector name
# Leave empty to auto-generate: twingate-pi-<hostname>
CONNECTOR_NAME=
# Auto-updates enabled/disabled
AUTO_UPDATE_ENABLED=true
# Update time (24-hour format)
AUTO_UPDATE_TIME=03:00- Docker installed and running
- 8GB+ free disk space
- Or: Linux system with sudo access (for manual build)
# Clone repository
git clone https://github.com/yourusername/pi-starter.git
cd pi-starter
# Build with Docker (handles everything automatically)
bash scripts/build-with-docker.sh
# Compress for distribution (optional)
xz -9 twingate-connector-pi.img# Download base Raspberry Pi OS image
wget https://downloads.raspberrypi.org/raspios_lite_arm64/images/raspios_lite_arm64-2025-12-04/2025-12-04-raspios-trixie-arm64-lite.img.xz
xz -d *.img.xz
# Run customization script
sudo bash scripts/customize-image.sh raspios-lite.img twingate-connector-pi.img
# Compress for distribution
xz -9 twingate-connector-pi.imgTo set a custom default password instead of "raspberry":
# Generate a password hash
bash scripts/generate-password-hash.sh
# Follow the instructions to update customize-image.shThe image includes configurable automatic updates:
- Default: Enabled, runs at 3:00 AM local time
- Managed by:
unattended-upgrades+ systemd timer - Scope: Only Twingate packages
- No reboot required: Connector gracefully restarts
Set the auto update flag to false in the twingate-config.txt file:
AUTO_UPDATE_ENABLED=falseOr disable after provisioning:
sudo systemctl stop apt-daily-upgrade.timer
sudo systemctl disable apt-daily-upgrade.timerEdit /etc/systemd/system/apt-daily-upgrade.timer.d/override.conf:
[Timer]
OnCalendar=
OnCalendar=*-*-* 04:00:00Then reload:
sudo systemctl daemon-reload
sudo systemctl restart apt-daily-upgrade.timer# View provisioning log
cat /boot/firmware/twingate-provision.log
# Check service status
sudo systemctl status twingate-firstboot
sudo systemctl status twingate-connector
# View service logs
sudo journalctl -u twingate-connector -fIf provisioning fails or you want to re-provision (including Connector in Admin Console):
# Remove provisioned flag and reboot
sudo rm /etc/twingate/.provisioned
sudo rebootConnector not showing in Admin Console:
- Check
/boot/firmware/twingate-provision.logfor errors - Verify API key is correct with required permissions
- Ensure Raspberry Pi has internet connection
- Check Twingate network name is correct
Service won't start:
# Check service logs
sudo journalctl -u twingate-connector -n 50
# Verify configuration
sudo cat /etc/twingate/connector.conf
# Test connectivity
ping -c 4 $(echo $TWINGATE_NETWORK).twingate.comThe repository includes automated image building:
- Daily Check: Runs at 2 / 3 AM UTC to check for new Connector, and Raspberry Pi OS versions
- Auto-Release: Creates GitHub release when new version detected
- Version Tracking: Prevents duplicate builds
- Checks Twingate repository for latest Connector version
- Only builds if version changed
- Generates SHA256 checksums
- Creates detailed release notes
- Compresses images with XZ
pi-starter/
βββ .github/
β βββ workflows/
β βββ build-image.yml # CI/CD pipeline
βββ scripts/
β βββ twingate-provision.sh # First-boot provisioning script
β βββ install-connector.sh # Connector installation for image build
β βββ customize-image.sh # Image customization script
β βββ build-with-docker.sh # Docker-based build script
β βββ generate-password-hash.sh # Password hash generator
βββ config/
β βββ twingate-config.txt.example # Configuration template
βββ Dockerfile # Docker build configuration
βββ README.md- API Key: Removed from config file after first boot provisioning
- Tokens: Stored in
/etc/twingate/connector.confwith 600 permissions - Logs: Contain no sensitive data
- Boot Partition: Accessible without root, don't leave API key in config after setup
- Default Credentials: Uses standard pi/raspberry - CHANGE IMMEDIATELY after first login
- SSH: Enabled by default for remote access
Best Practices:
- Change default password immediately after first login:
passwd - Use unique Connector names for tracking
- Rotate API keys periodically
- Use SSH keys instead of password authentication
MIT License - see LICENSE file for details
Need Help? Reach out on Twingate's subreddit