Important
This branch reflects version 4.0.0, which is in active development with a complete redesign of the UI. The live demo at demo.portkey.page shows this in-development version, not the latest stable release. The screenshots later in this document still show the previous stable version and have not yet been updated to reflect the 4.0.0 redesign.
- ๐ Shows a collection of links acting as startup page or similar.
- ๐ Includes a search box with configurable keyword support and fuzzy matching.
- ๐ Portals can be organised into named groups/sections on the home page.
- ๐ Optional multi-column grid layout for portals and groups (configurable column count, mobile-responsive).
- ๐ Can be configured easily by modifying only one file.
- ๐๏ธ Also supports adding smaller custom pages.
- ๐ Dark and light mode available.
- ๐ชฐ Very lightweight application with Docker images available.
You can also find a demo here
Click to view screenshots
Download the portkey file for your OS. Probably to a location that is in your PATH, so you can use it right away.
- Create a
config.ymlor use the example configuration from this repository and configure it as you want.
You can find a detailed explanation of all configuration options here.
- Start the application with
portkey --config-path=<dir_to_config_yml>. Providing the path to the configuration file is optional if it's in the working directory. - Open your browser at the defined host and port. Default is http://localhost:3000
portkey is configured with a single configuration file called config.yml. You can pass its location using the --config-path argument.
You can also overwrite configuration values from file by using environment variables in uppercase and prefixed with PORTKEY_. For instance, the configuration key host can be passed as environment variable PORTKEY_HOST.
The config.yml contains the following configuration options:
# Can be changed to reduce or increase logs. Values could be "ERROR", "WARN", "INFO" (default) or "DEBUG".
logLevel: INFO
# If enabled logs are in JSON format.
logJson: false
# Set the host where the application should bind to.
host: localhost
# Set the port where the application should bind to.
port: 3000
# Set the context path (aka base-url) portkey is hosted under. Must not be specified unless you're using a reverse proxy and are hosting portkey under a directory. If that's the case then you can set this value to e.g. /portkey or whatever the directory is called. Note that the forward slash (/) in the beginning is required!
contextPath: ""
# Enables the HTTP server that serves metrics that can be scraped by e.g. Prometheus.
enableMetrics: false
# Set the host where the metrics server should bind to.
metricsHost: localhost
# Set the port where the metrics server should bind to.
metricsPort: 3030# Title of the application shown in the browser tab and on the front page.
title: "portkey"
# Allows to hide the title.
hideTitle: false
# Allows adding additional scripts/stylesheets etc. to the HTML header. Can be useful for analytics or smaller style modifications.
headerAddition: |-
<script async src="https://analytics.example.com"></script>
# Footer (HTML support) that is shown on every page.
# Remember that Tailwind CSS classes used here do only work if already used somewhere else in the application because the bundler couldn't look here!
footer: |-
<p>This is a footer!</p>
# Defines whether portkey's application icon should be shown at the top left of the front page.
showTopIcon: true
# If true keywords of portals are shown as tooltip on hover.
showKeywordsAsTooltips: false
# If true all links are sorted alphabetically when shown on the front page. Otherwise they are shown in the order they are defined.
sortAlphabetically: false
# If true search query is also compared to portals and keywords using Levenshtein string metric.
searchWithStringSimilarity: false
# Minimum required similarity for results when 'searchWithStringSimilarity' is 'true'. Must be between '0.0' (0%) and '1.0' (100%).
minimumStringSimilarity: 0.5
# If true, search bar is hidden. Can be useful with a low amount of portals making the search unnecessary.
hideSearchBar: false
# Number of columns for the grid layout (0 = disabled/vertical).
# On mobile (<768px) the layout always falls back to vertical stacking.
# When groups exist, each group occupies one grid cell.
# When no groups, portals are distributed across N columns.
layoutColumns: 0
# Self-hostable favicon fetch service URL.
faviconServiceURL: https://favicon.vemetric.com
# On-disk favicon cache directory. Mountable as a Docker volume for persistence across restarts.
faviconCacheDir: ./favicon-cache
# Set to true to bypass local favicon caching and fetch directly from the remote service.
faviconCacheDisabled: false
# Directory for custom icon files (SVG, PNG). Files are served at /_/icons/<filename>.
# Mountable as a Docker volume. Requires creating the directory and placing icon files.
customIconsDir: ./icons# Defines a list of portals (links) that have additional attributes defining their appearance.
portals:
# Name of the link
- title: example
# (Optional) Icon shown in front of the title. Supports multiple formats:
# - Emoji: icon: "๐"
# - Custom SVG/PNG: icon: /_/icons/github.svg (place file in customIconsDir)
# - Absolute URL: icon: https://example.com/icon.png
# - Data URI: icon: data:image/svg+xml,%3Csvg...
# If empty, the global favicon is used for external links (cached automatically)
# and a file icon is shown for internal pages.
# Link where the portal will lead to (can be relative for custom pages or absolute otherwise)
link: https://example.com/
# Additional keywords used by the search feature.
keywords:
- url
- example
# (Optional) Group name for organising portals into sections on the home page.
# Portals sharing the same group value are rendered together under a labelled heading.
# Portals without a group are shown ungrouped at the bottom. Groups appear in the
# order the first portal of each group is defined.
group: My GroupTip: When a search query is active, portals are still grouped by their group field. Groups with no matching portals are hidden.
# Defines a list of custom pages that are made available at the defined paths.
# Important: These are not automatically added to the list of portals and have to be added manually!
# This may be changed in the future.
pages:
# Heading for the custom page. Shown in browser tab and as heading on the page.
- heading: Custom
# If true, shows the (optionally configured) subtitle also on this page.
showSubtitle: true
# Path where the custom page will be available.
path: /custom
# Content of the custom page and it supports using HTML.
# The same CSS rules apply as for the footer!
content: |-
This is a <em>custom page</em><br>
It also supports using <strong>HTML</strong>!Metrics can be enabled with the enableMetrics configuration key and are served on a dedicated HTTP server. By default they are served on http://localhost:3030/metrics. Use this address to configure your tool of choice (e.g. Prometheus) to scrape the exported metrics.
Besides the default metrics provided by the Prometheus instrumentation library for Go applications , the following additional metrics are provided:
# HELP portkey_page_handler_requests_total Total number of HTTP requests by page.
# TYPE portkey_page_handler_requests_total counter
portkey_page_handler_requests_total{path="<page_path>"} 0
# HELP portkey_portal_handler_requests_total Total number of HTTP requests by portal.
# TYPE portkey_portal_handler_requests_total counter
portkey_portal_handler_requests_total{portal="<portal_title>"} 0
# HELP portkey_search_requests_no_results_total Total number of HTTP requests for search with no results.
# TYPE portkey_search_requests_no_results_total counter
portkey_search_requests_no_results_total 0
# HELP portkey_search_requests_with_results_total Total number of HTTP requests for search with at least one result.
# TYPE portkey_search_requests_with_results_total counter
portkey_search_requests_with_results_total 0
# HELP portkey_version_info Version information about portkey.
# TYPE portkey_version_info gauge
portkey_version_info{buildTime="2024.10.09_17:29:19",commitHash="4fd1a0f",goVersion="1.23.1",version="dev"} 1
There are also Docker images available at Docker hub that you can use. You can start a container with the following command:
# Assumes that there is a config.yml in the current directory.
# It is probably better to use a specific version than 'latest'.
docker run --rm -it \
-v $(PWD)/config.yml:/opt/config.yml \
-v $(PWD)/favicon-cache:/opt/favicon-cache \
-v $(PWD)/icons:/opt/icons \
-e PORTKEY_FAVICONCACHEDIR=/opt/favicon-cache \
-e PORTKEY_CUSTOMICONSDIR=/opt/icons \
-p 3000:3000 \
codehat/portkey:latestportkey is a Go application. You can install its dependencies with go mod download.
The frontend dependencies (e.g. TailwindCSS, AlpineJS) can be installed with npm install --include dev.
They can be watched with npm run watch and built with npm run build.
A library called templ is used for the templates. To generate the .go files from the templates, it has to be installed. templ is installed using go tools and can be invoked with:
go tool templAfterwards you can generate the compiled templates with templ generate.
Live reloading is possible by installing air and calling air.
air is installed using go tools and can be invoked with:
go tool air



