Repository: robinmanuelthiel/speedtest
Branch: main
Commit: 9b93de5350c4
Files: 7
Total size: 15.2 KB
Directory structure:
gitextract_905bh62o/
├── .github/
│ └── workflows/
│ └── release.yaml
├── Dockerfile
├── LICENSE
├── README.md
├── demo/
│ ├── dashboard.json
│ └── docker-compose.yaml
└── speedtest.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/release.yaml
================================================
name: Release
on:
workflow_dispatch:
inputs:
version:
description: "The version to release"
default: "1.0.0"
required: true
jobs:
release:
name: Update Container Images
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Login to DockerHub
uses: docker/login-action@v1.10.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@master
with:
platforms: all
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- run: |
docker buildx build \
--push \
--platform linux/arm/v7,linux/arm64/v8,linux/amd64 \
--tag robinmanuelthiel/speedtest:${{ github.event.inputs.version }} \
--tag robinmanuelthiel/speedtest:latest \
.
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ github.event.inputs.version }}
name: Version ${{ github.event.inputs.version }}
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: Dockerfile
================================================
FROM debian:bullseye
# Install basics
RUN apt-get update && apt-get install -y curl jq gnupg1 apt-transport-https dirmngr
# Install speedtest cli
RUN curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | bash
RUN apt-get install -y speedtest
COPY ./speedtest.sh .
CMD ["./speedtest.sh"]
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Robin-Manuel Thiel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Internet Speed Test in a Container
[](https://hub.docker.com/r/robinmanuelthiel/speedtest/)
Check your internet bandwidth using the [Speedtest CLI](https://www.speedtest.net/apps/cli) from a Docker container. You can configure the tool to run periodically and save the results to an InfluxDB for visualization or long-term records.
```bash
docker run --rm robinmanuelthiel/speedtest:latest
```
The result will then look like this:
```bash
Running a Speed Test...
Your download speed is 334 Mbps (29284399 Bytes/s).
Your upload speed is 42 Mbps (4012944 Bytes/s).
Your ping is 6.223 ms.
Speedtest took 23 seconds."
```
## Configuration
| Environment variable | Default value | Description |
|-----------------------|-------------------------|--------------------------------------------------------------------------------------|
| `LOOP` | `false` | Run Speedtest in a loop |
| `LOOP_DELAY` | `60` | Delay in seconds between the runs |
| `DB_SAVE` | `false` | Save values to InfluxDB |
| `DB_HOST` | `http://localhost:8086` | InfluxDB Hostname |
| `DB_NAME` | `speedtest` | InfluxDB Database name |
| `DB_USERNAME` | `admin` | InfluxDB Username |
| `DB_PASSWORD` | `password` | InfluxDB Password |
| `SPEEDTEST_SERVER_ID` | none | Specify a server from the server list using its ID |
| `SPEEDTEST_HOSTNAME` | none | Specify a server, from the server list, using its host's fully qualified domain name |
To get the available Server IDs, sponsors, and hostnames from speedtest run:
```bash
curl -s https://cli.speedtest.net/api/cli/config | jq -r '.servers[] | "id: \(.id), sponsor: \(.sponsor), host: \(.host)"'
```
## Grafana and InfluxDB

For a full visualization and long term tracking, I recommend InfluxDB as a time-series database and Grafana as a dashboard engine. Both come in Docker containers, so the whole setup can be achieved by starting a Docker Compose file.
```yaml
version: "3"
services:
grafana:
image: grafana/grafana:7.5.2
restart: always
ports:
- 3000:3000
volumes:
- grafana:/var/lib/grafana
depends_on:
- influxdb
influxdb:
image: influxdb:1.8.3
restart: always
volumes:
- influxdb:/var/lib/influxdb
ports:
- 8083:8083
- 8086:8086
environment:
- INFLUXDB_ADMIN_USER="admin"
- INFLUXDB_ADMIN_PASSWORD="password"
- INFLUXDB_DB="speedtest"
speedtest:
image: robinmanuelthiel/speedtest:latest
restart: always
environment:
- LOOP=true
- LOOP_DELAY=1800
- DB_SAVE=true
- DB_HOST=http://influxdb:8086
- DB_NAME=speedtest
- DB_USERNAME=admin
- DB_PASSWORD=password
privileged: true # Needed for 'sleep' in the loop
depends_on:
- influxdb
volumes:
grafana:
influxdb:
```
To configure Grafana, we need to **add InfluxDB as a data source first** and then create a dashboard with the upload and download values. You can find a demo dashboard configuration in the [/demo](/demo) folder.
> **Hint:** The speedtest outputs values as bytes per second. Make sure to divide all values by 125000 in your dashboard to get the Mbps values.
================================================
FILE: demo/dashboard.json
================================================
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": 1,
"links": [],
"panels": [
{
"aliasColors": {},
"bars": false,
"cacheTimeout": null,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fill": 1,
"fillGradient": 2,
"gridPos": {
"h": 13,
"w": 24,
"x": 0,
"y": 0
},
"hiddenSeries": false,
"id": 2,
"legend": {
"alignAsTable": false,
"avg": true,
"current": false,
"hideEmpty": false,
"max": true,
"min": true,
"rightSide": false,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 2,
"links": [],
"nullPointMode": "connected",
"options": {
"dataLinks": []
},
"percentage": false,
"pluginVersion": "6.7.2",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"alias": "download",
"groupBy": [
{
"params": [
"$__interval"
],
"type": "time"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"hide": false,
"measurement": "download",
"orderByTime": "ASC",
"policy": "default",
"query": "SELECT mean(\"value\") FROM \"download\" WHERE $timeFilter GROUP BY time($__interval) fill(null)",
"rawQuery": false,
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
},
{
"params": [
" / 125000"
],
"type": "math"
}
]
],
"tags": []
},
{
"alias": "upload",
"groupBy": [
{
"params": [
"$__interval"
],
"type": "time"
},
{
"params": [
"null"
],
"type": "fill"
}
],
"measurement": "upload",
"orderByTime": "ASC",
"policy": "default",
"refId": "B",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"value"
],
"type": "field"
},
{
"params": [],
"type": "mean"
},
{
"params": [
"/125000"
],
"type": "math"
}
]
],
"tags": []
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "m-Net",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "Mbits",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": false
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"refresh": "5m",
"schemaVersion": 22,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-12h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "",
"title": "Bandwidth",
"uid": "M27juGmgk",
"variables": {
"list": []
},
"version": 5
}
================================================
FILE: demo/docker-compose.yaml
================================================
version: "3"
services:
grafana:
image: grafana/grafana:7.5.2
restart: always
ports:
- 3000:3000
volumes:
- grafana:/var/lib/grafana
depends_on:
- influxdb
influxdb:
image: influxdb:1.8.3
restart: always
volumes:
- influxdb:/var/lib/influxdb
ports:
- 8083:8083
- 8086:8086
environment:
- INFLUXDB_ADMIN_USER="admin"
- INFLUXDB_ADMIN_PASSWORD="password"
- INFLUXDB_DB="speedtest"
speedtest:
image: robinmanuelthiel/speedtest:latest
build:
context: ../
dockerfile: Dockerfile
restart: always
environment:
- LOOP=true
- LOOP_DELAY=300
- DB_SAVE=true
- DB_HOST=http://influxdb:8086
- DB_NAME=speedtest
- DB_USERNAME=admin
- DB_PASSWORD=password
privileged: true # Needed for 'sleep' in the loop
depends_on:
- influxdb
volumes:
grafana:
influxdb:
================================================
FILE: speedtest.sh
================================================
#!/bin/sh
# These values can be overwritten with env variables
LOOP="${LOOP:-false}"
LOOP_DELAY="${LOOP_DELAY:-60}"
DB_SAVE="${DB_SAVE:-false}"
DB_HOST="${DB_HOST:-http://localhost:8086}"
DB_NAME="${DB_NAME:-speedtest}"
DB_USERNAME="${DB_USERNAME:-admin}"
DB_PASSWORD="${DB_PASSWORD:-password}"
SPEEDTEST_HOSTNAME="${SPEEDTEST_HOSTNAME}"
SPEEDTEST_SERVER_ID="${SPEEDTEST_SERVER_ID}"
run_speedtest()
{
DATE=$(date +%s)
HOSTNAME=$(hostname)
START_TIME=$(date +%s)
# Start speed test
if [ -n "$SPEEDTEST_SERVER_ID" ]; then
echo "Running a Speed Test with Server ID $SPEEDTEST_SERVER_ID... "
JSON=$(speedtest --accept-license --accept-gdpr -f json -s $SPEEDTEST_SERVER_ID) || JSON=""
elif [ -n "$SPEEDTEST_HOSTNAME" ]; then
echo "Running a Speed Test with Hostname $SPEEDTEST_HOSTNAME... "
JSON=$(speedtest --accept-license --accept-gdpr -f json -o $SPEEDTEST_HOSTNAME) || JSON=""
else
echo "Running a Speed Test with default host... "
JSON=$(speedtest --accept-license --accept-gdpr -f json) || JSON=""
fi
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# If JSON is empty, then the speedtest command failed.
# In this case set the values to 0.
if [ -z "$JSON" ]; then
DOWNLOAD=0
UPLOAD=0
PING=0
else
# Fetch the values from the JSON output and convert them to the correct units (converts null to 0)
DOWNLOAD="$(echo $JSON | jq -r '.download.bandwidth // 0')"
UPLOAD="$(echo $JSON | jq -r '.upload.bandwidth // 0')"
PING="$(echo $JSON | jq -r '.ping.latency // 0')"
fi
echo "Your download speed is $(($DOWNLOAD / 125000 )) Mbps ($DOWNLOAD Bytes/s)."
echo "Your upload speed is $(($UPLOAD / 125000 )) Mbps ($UPLOAD Bytes/s)."
echo "Your ping is $PING ms."
echo "Speedtest took $DURATION seconds."
# Save results in the database
if $DB_SAVE;
then
echo "Saving values to database..."
curl -s -S -XPOST "$DB_HOST/write?db=$DB_NAME&precision=s&u=$DB_USERNAME&p=$DB_PASSWORD" \
--data-binary "download,host=$HOSTNAME value=$DOWNLOAD $DATE"
curl -s -S -XPOST "$DB_HOST/write?db=$DB_NAME&precision=s&u=$DB_USERNAME&p=$DB_PASSWORD" \
--data-binary "upload,host=$HOSTNAME value=$UPLOAD $DATE"
curl -s -S -XPOST "$DB_HOST/write?db=$DB_NAME&precision=s&u=$DB_USERNAME&p=$DB_PASSWORD" \
--data-binary "ping,host=$HOSTNAME value=$PING $DATE"
echo "Values saved."
fi
}
# Check for input errors
if [ -n "$SPEEDTEST_SERVER_ID" ] && [ -n "$SPEEDTEST_HOSTNAME" ]; then
echo >&2 "[error] Only one server option can be specified, please use one of ['SPEEDTEST_SERVER_ID' or 'SPEEDTEST_HOSTNAME']"
exit 1
fi
if $LOOP;
then
echo "Running speedtest in a loop until stopped..."
while :
do
run_speedtest
echo "Running next test in ${LOOP_DELAY}s..."
sleep $LOOP_DELAY
done
else
run_speedtest
fi
gitextract_905bh62o/ ├── .github/ │ └── workflows/ │ └── release.yaml ├── Dockerfile ├── LICENSE ├── README.md ├── demo/ │ ├── dashboard.json │ └── docker-compose.yaml └── speedtest.sh
Condensed preview — 7 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (17K chars).
[
{
"path": ".github/workflows/release.yaml",
"chars": 1298,
"preview": "name: Release\n\non:\n workflow_dispatch:\n inputs:\n version: \n description: \"The version to release\""
},
{
"path": "Dockerfile",
"chars": 325,
"preview": "FROM debian:bullseye\n\n# Install basics\nRUN apt-get update && apt-get install -y curl jq gnupg1 apt-transport-https dirmn"
},
{
"path": "LICENSE",
"chars": 1075,
"preview": "MIT License\n\nCopyright (c) 2020 Robin-Manuel Thiel\n\nPermission is hereby granted, free of charge, to any person obtainin"
},
{
"path": "README.md",
"chars": 4085,
"preview": "# Internet Speed Test in a Container\n\n[. The extraction includes 7 files (15.2 KB), approximately 4.1k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.