Command-line interface for the Netcup Server Control Panel (SCP) REST API.
Manage servers, rDNS, snapshots, rescue systems, firewall policies, SSH keys, VLANs, and more from the terminal. Uses OAuth2 device-code flow; credentials are stored locally and refreshed automatically.
| Python | 3.9+ |
| License | MIT |
| API | SCP REST API · OpenAPI 2026.0519.150426 |
Why
netcupand notscp? The command is namednetcupto avoid clashing with the standard Unix scp (secure copy) command.
- Overview
- Requirements
- Installation
- Quick start
- Authentication
- Command reference
- Configuration
- Output and exit codes
- Troubleshooting
- API compatibility
- Project structure
- Getting help
- License
netcup CLI talks to the netcup SCP REST API. It supports:
- Servers — List, get, power on/off, set hostname/nickname; manage disks, interfaces, firewall, ISO, snapshots, rescue system, metrics, logs, image setup, guest agent, storage optimization.
- rDNS — Get, set, and delete reverse DNS for IPv4 and IPv6.
- Tasks — List (with filters), get, and cancel async tasks.
- Users — Current user info, get/update user; manage failover IPs, firewall policies, SSH keys, VLANs, user images/ISOs, logs.
- VLANs — Get VLAN by ID (standalone).
- Maintenance — Ping API and maintenance window info.
All commands that need a user context (e.g. users failoverips list) use the current user from your token by default; override with --user-id <id> when needed.
- Python 3.9 or newer
- Network access to
https://www.servercontrolpanel.de - A netcup SCP account (device-code login in the browser)
pip install netcup-clicd netcup-cli
pip install -e .After installation, the netcup command is available.
Development (lint, format, test) — using uv (recommended):
uv sync --all-extras # create venv + install deps including dev
uv run ruff check src tests # lint
uv run ruff format src tests # format
uv run ruff check src tests --fix # auto-fix lint
uv run pytest # testsWithout uv: pip install -e ".[dev]" then run ruff and pytest as above.
netcup --helpcd netcup-cli
pip install -r requirements.txt
PYTHONPATH=src python -m netcup_cli --helppipx install path/to/netcup-cli
# or after cloning:
pipx install -e path/to/netcup-cli-
Log in (opens browser for device code):
netcup auth login
Complete the flow in the browser; the CLI stores a refresh token under
~/.config/netcup-cli/. -
Check API:
netcup maintenance ping # OK -
List servers:
netcup servers list
-
Inspect one server:
netcup servers get <server_id>
-
Power control:
netcup servers power <server_id> on netcup servers power <server_id> off --option POWEROFF
-
rDNS:
netcup rdns ipv4 get 1.2.3.4 netcup rdns ipv4 set 1.2.3.4 host.example.com -
Current user:
netcup users info
For the full command set, see Command reference.
- Method: OAuth2 device code flow (no password in the CLI).
- Stored credential: Only the refresh token is saved; access tokens are obtained on demand and not written to disk.
- Location:
~/.config/netcup-cli/credentials(or$XDG_CONFIG_HOME/netcup-cli/credentialsif set). File format:{"refresh_token": "…"}. - Lifetime: Refresh token remains valid as long as it is used at least once every 30 days (per SCP API).
Commands:
| Command | Description |
|---|---|
netcup auth login |
Start device-code login; optionally --no-save to avoid storing the token. |
netcup auth logout |
Remove the stored credentials file. |
netcup auth revoke |
Revoke the current refresh token on the server and delete the local file. |
netcup auth show |
Print the path of the credentials file and whether it exists. |
Security:
- The credentials file should be readable only by the current user (the CLI sets mode
0600when possible). - Do not share the credentials file or the refresh token.
- Use
netcup auth revokeif the token may have been exposed.
--help,-h— Show help for the command or group.--version— Show netcup CLI version.
| Command | Description |
|---|---|
auth login [--no-save] |
Log in via device code; store refresh token unless --no-save. |
auth logout |
Remove stored credentials. |
auth revoke |
Revoke refresh token and remove credentials. |
auth show |
Show credentials file path and existence. |
| Command | Arguments / options | Description |
|---|---|---|
servers list |
[--limit N] [--offset N] [--ip IP] [--name NAME] [-q QUERY] |
List servers (optional filters). |
servers get <server_id> |
[--no-live] |
Get one server; --no-live skips live info. |
servers power <server_id> on|off |
[--option POWERCYCLE|RESET|POWEROFF] |
Power on/off or power cycle. |
servers set-hostname <server_id> <hostname> |
Set server hostname. | |
servers set-nickname <server_id> <nickname> |
Set server nickname. |
Disks: servers disks <server_id>
| Subcommand | Arguments | Description |
|---|---|---|
list |
<server_id> |
List disks. |
get |
<server_id> <disk_name> |
Get one disk. |
supported-drivers |
<server_id> |
List supported storage drivers. |
set-driver |
<server_id> VIRTIO|VIRTIO_SCSI|IDE|SATA |
Set disk driver. |
format |
<server_id> <disk_name> |
Format disk (data loss). |
Interfaces: servers interfaces <server_id>
| Subcommand | Arguments / options | Description |
|---|---|---|
list |
<server_id> [--no-rdns] |
List interfaces. |
get |
<server_id> <mac> [--no-rdns] |
Get interface by MAC. |
create |
<server_id> --vlan-id ID --driver DRIVER |
Create interface (VLAN); driver: VIRTIO, E1000, E1000E, RTL8139, VMXNET3. |
update |
<server_id> <mac> --body JSON |
Update interface (e.g. driver). |
delete |
<server_id> <mac> |
Delete interface. |
Firewall (per interface): servers firewall <server_id> <mac>
| Subcommand | Description |
|---|---|
get [--consistency-check] |
Get firewall config. |
put --body JSON |
Set firewall (copiedPolicies, userPolicies, active). |
reapply |
Reapply firewall. |
restore-copied-policies |
Restore copied policies. |
ISO: servers iso <server_id>
| Subcommand | Options | Description |
|---|---|---|
get |
Get attached ISO. | |
attach |
--iso-id ID or --user-iso NAME; [--boot-cdrom] |
Attach ISO. |
detach |
Detach ISO. | |
images |
List available ISO images for server. |
Snapshots: servers snapshots <server_id>
| Subcommand | Arguments / options | Description |
|---|---|---|
list |
<server_id> |
List snapshots. |
get |
<server_id> <name> |
Get one snapshot. |
create |
<server_id> --name NAME [--description DESC] [--disk-name NAME] [--online] |
Create snapshot. |
delete |
<server_id> <name> |
Delete snapshot. |
export |
<server_id> <name> |
Export snapshot. |
revert |
<server_id> <name> |
Revert to snapshot. |
dryrun |
<server_id> [--disk-name NAME] [--online] |
Check if snapshot creation is possible. |
Rescue system: servers rescue <server_id>
| Subcommand | Description |
|---|---|
get |
Get rescue system status. |
activate |
Activate rescue system. |
deactivate |
Deactivate rescue system. |
Metrics: servers metrics <server_id>
| Subcommand | Options | Description |
|---|---|---|
cpu |
[--hours N] |
CPU metrics. |
disk |
[--hours N] |
Disk metrics. |
network |
[--hours N] |
Network metrics. |
packet |
[--hours N] |
Network packet metrics. |
Other server commands
| Command | Arguments / options | Description |
|---|---|---|
servers logs list |
<server_id> [--limit N] [--offset N] |
Server logs. |
servers guest-agent get |
<server_id> |
Guest agent data. |
servers image flavours |
<server_id> |
Image flavours for setup. |
servers image setup |
<server_id> --body JSON |
Setup image (JSON body per API). |
servers user-image setup |
<server_id> --name NAME [--disk-name NAME] [--email-notification] |
Setup user image. |
servers storage-optimization start |
<server_id> [--disks NAME...] [--start-after] |
Start storage optimization. |
| Command | Example |
|---|---|
rdns ipv4 get <ip> |
netcup rdns ipv4 get 1.2.3.4 |
rdns ipv4 set <ip> <rdns> |
netcup rdns ipv4 set 1.2.3.4 host.example.com |
rdns ipv4 delete <ip> |
|
rdns ipv6 get <ip> |
|
rdns ipv6 set <ip> <rdns> |
|
rdns ipv6 delete <ip> |
| Command | Options | Description |
|---|---|---|
tasks list |
[--limit N] [--offset N] [-q QUERY] [--server-id ID] [--state STATE] |
List tasks. |
tasks get <uuid> |
Get one task. | |
tasks cancel <uuid> |
Cancel task. |
| Command | Options | Description |
|---|---|---|
users info |
Current user (from token). | |
users get <user_id> |
Get user by ID. | |
users update <user_id> --body JSON |
Update user (UserSave schema). |
All commands below support --user-id <id>; if omitted, the current user is used.
Failover IPs: users failoverips v4|v6
| Subcommand | Example |
|---|---|
v4 list |
[--ip IP] [--server-id ID] |
v4 route <id> <server_id> |
Route failover IPv4 to server. |
v6 list |
Same options as v4. |
v6 route <id> <server_id> |
Route failover IPv6 to server. |
Firewall policies: users firewall-policies
| Subcommand | Options |
|---|---|
list |
[--limit N] [--offset N] [-q QUERY] |
get <policy_id> |
[--with-servers-count] |
create --body JSON |
JSON: name, optional description, rules. |
update <policy_id> --body JSON |
|
delete <policy_id> |
SSH keys: users ssh-keys
| Subcommand | Options |
|---|---|
list |
|
add --name NAME --key KEY |
Add public key. |
delete <key_id> |
VLANs: users vlans
| Subcommand | Options |
|---|---|
list |
[--server-id ID] |
get <vlan_id> |
|
update <vlan_id> <name> |
User images (S3): users images
| Subcommand | Description |
|---|---|
list |
List user images. |
delete <key> |
Delete image by key. |
download-url <key> |
Get presigned download URL. |
User ISOs (S3): users isos
| Subcommand | Description |
|---|---|
list |
List user ISOs. |
delete <key> |
Delete ISO by key. |
download-url <key> |
Get presigned download URL. |
User logs: users logs list [--limit N] [--offset N]
| Command | Description |
|---|---|
vlans get <vlan_id> |
Get VLAN by ID (no user scope). |
| Command | Description |
|---|---|
maintenance ping |
Check API availability (returns OK). |
maintenance info |
Maintenance window information. |
- Path:
~/.config/netcup-cli/credentials(Linux/macOS). On Windows,%APPDATA%\netcup-cli\credentialsis not used by default; the CLI uses$XDG_CONFIG_HOMEif set, otherwise~/.config. - Override directory: Set
XDG_CONFIG_HOME(e.g.export XDG_CONFIG_HOME=$HOME/.config). - Format: JSON with a single key:
{"refresh_token": "…"}. Do not edit manually unless you know the token value.
The CLI uses these base URLs (not configurable via env or file):
- Auth:
https://www.servercontrolpanel.de/realms/scp/protocol/openid-connect - API root:
https://www.servercontrolpanel.de/scp-core/api - API v1:
https://www.servercontrolpanel.de/scp-core/api/v1
To point at another environment, you would need to change the code in netcup_cli/config.py.
- Success: Exit code
0. List/get commands print JSON to stdout (pretty-printed). - Failure: Exit code
1. Error message is printed to stderr (and often in red when run in a TTY). - JSON: All API responses that return JSON are printed as-is (indented). Use
jqfor further processing if needed, e.g.netcup servers list | jq '.[].name'.
- Cause: The server could not satisfy the
Acceptheader (e.g. endpoint returnstext/plainbut client asked forapplication/json). - Relevant command:
maintenance ping— fixed in current version by requestingAccept: text/plainfor that call. Ensure you use the latest code.
- Cause: No refresh token found in the credentials file.
- Fix: Run
netcup auth loginand complete the browser flow. Ensure the credentials file exists at the path shown bynetcup auth show.
- Cause: Refresh token expired or was revoked (e.g. after 30 days of no use, or revoked in SCP account).
- Fix: Run
netcup auth loginagain. If needed, revoke old access in SCP: Account → Applications → scp → Remove access, then log in again.
- Cause: You declined the authorization in the browser or closed the page before approving.
- Fix: Run
netcup auth loginagain and approve the application in the browser.
- Cause: You did not complete the browser step within the validity window (e.g. 600 seconds).
- Fix: Run
netcup auth loginagain and complete the flow promptly.
- Cause: Resource not found, validation error, or service temporarily unavailable (e.g. maintenance).
- Action: Check IDs and request body; for 503, retry later or check maintenance info:
netcup maintenance info.
- Spec: The CLI is built against the SCP REST API as described in the OpenAPI spec (version 2026.0519.150426 in the bundled
openapi.json). - Coverage: Most endpoints from the spec are implemented (servers, rDNS, tasks, users, failover IPs, firewall policies, SSH keys, VLANs, images/ISOs, logs, maintenance). Multipart upload for user images/ISOs is not implemented in the CLI.
- Breaking changes: If the API introduces breaking changes, the CLI may need updates. Check the netcup SCP API forum and release notes.
The file openapi.json in the project root is a snapshot of the spec fetched from:
curl -s -X GET 'https://www.servercontrolpanel.de/scp-core/api/v1/openapi' -H 'accept: application/json' -o openapi.jsonYou can refresh it to align with the latest API. The CLI does not read this file at runtime; it is for reference and code generation.
The bundled spec documents POST /v1/openapi/mcp (tag Miscellaneous; some explorers label it like #/Miscellaneous/post_api_v1_openapi_mcp). Summary: SCP OpenAPI Spec MCP Server.
Purpose (from the spec): SCP (Server Control Panel) API Explorer — OpenAPI specification explorer for the netcup SCP REST API. It provides documentation, schema definitions, and guidance for building SCP API clients and scripts. It does not execute SCP API calls directly.
Full URL: https://www.servercontrolpanel.de/scp-core/api/v1/openapi/mcp
Use this with an MCP-capable client if you want explore-only access to the API surface. It uses the same https://www.servercontrolpanel.de/scp-core/api/v1 base as other authenticated routes; this CLI does not ship a wrapper for the MCP protocol (JSON-RPC message exchange over HTTP).
netcup-cli/
├── pyproject.toml # Package metadata, dependencies, entry point
├── requirements.txt # requests, click
├── README.md # This file
├── openapi.json # Snapshot of SCP OpenAPI spec
└── src/netcup_cli/
├── __init__.py # Version
├── config.py # URLs, credential path
├── auth.py # Device code, refresh, revoke, load/save credentials
├── client.py # HTTP client (Bearer token, Accept header)
├── exceptions.py # SCPError, AuthError, APIError, ConfigError
├── output.py # JSON formatting
├── api/ # API layer (one module per resource)
│ ├── base.py
│ ├── maintenance.py
│ ├── rdns.py
│ ├── servers.py
│ ├── servers_disks.py
│ ├── servers_interfaces.py
│ ├── servers_iso.py
│ ├── servers_snapshots.py
│ ├── servers_rescue.py
│ ├── servers_metrics.py
│ ├── servers_logs.py
│ ├── servers_image.py
│ ├── servers_guest.py
│ ├── servers_gpu.py
│ ├── servers_storage.py
│ ├── tasks.py
│ ├── users.py
│ ├── user_failoverips.py
│ ├── user_firewall_policies.py
│ ├── user_ssh_keys.py
│ ├── user_vlans.py
│ ├── user_images.py
│ ├── user_isos.py
│ └── user_logs.py
└── cli/ # Click commands
├── main.py # Entry point, group registration
├── helpers.py # user_id resolution
├── auth_cmd.py
├── servers_cmd.py
├── servers_disks_cmd.py
├── servers_interfaces_cmd.py
├── servers_iso_cmd.py
├── servers_snapshots_cmd.py
├── servers_rescue_cmd.py
├── servers_metrics_cmd.py
├── servers_misc_cmd.py
├── rdns_cmd.py
├── tasks_cmd.py
├── users_cmd.py
├── users_resources_cmd.py
├── vlans_cmd.py
└── maintenance_cmd.py
- General:
netcup --help - Command group:
netcup servers --help,netcup users --help, etc. - Specific command:
netcup servers power --help,netcup rdns ipv4 set --help - Version:
netcup --version
MIT. See the LICENSE file in the project repository for the full license text.